FEATURE: Support designating multiple groups as mods on category (#28655)
Currently, categories support designating only 1 group as a moderation group on the category. This commit removes the one group limitation and makes it possible to designate multiple groups as mods on a category. Internal topic: t/124648.
This commit is contained in:
parent
7092d88ee4
commit
280adda09c
|
@ -120,11 +120,10 @@
|
||||||
{{#if this.siteSettings.enable_category_group_moderation}}
|
{{#if this.siteSettings.enable_category_group_moderation}}
|
||||||
<section class="field reviewable-by-group">
|
<section class="field reviewable-by-group">
|
||||||
<label>{{i18n "category.reviewable_by_group"}}</label>
|
<label>{{i18n "category.reviewable_by_group"}}</label>
|
||||||
<GroupSelector
|
<GroupChooser
|
||||||
@groupFinder={{this.groupFinder}}
|
@content={{this.site.groups}}
|
||||||
@single="true"
|
@value={{this.category.moderating_group_ids}}
|
||||||
@groupNames={{this.category.reviewable_by_group_name}}
|
@onChange={{this.onCategoryModeratingGroupsChange}}
|
||||||
@placeholderKey="category.review_group_name"
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { and, empty } from "@ember/object/computed";
|
||||||
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
||||||
import { setting } from "discourse/lib/computed";
|
import { setting } from "discourse/lib/computed";
|
||||||
import { SEARCH_PRIORITIES } from "discourse/lib/constants";
|
import { SEARCH_PRIORITIES } from "discourse/lib/constants";
|
||||||
import Group from "discourse/models/group";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
|
@ -50,10 +49,6 @@ export default class EditCategorySettings extends buildCategoryPanel(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
groupFinder(term) {
|
|
||||||
return Group.findAll({ term, ignore_automatic: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
availableViews() {
|
availableViews() {
|
||||||
return [
|
return [
|
||||||
|
@ -146,4 +141,9 @@ export default class EditCategorySettings extends buildCategoryPanel(
|
||||||
let seconds = minutes ? minutes * 60 : null;
|
let seconds = minutes ? minutes * 60 : null;
|
||||||
this.set("category.default_slow_mode_seconds", seconds);
|
this.set("category.default_slow_mode_seconds", seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onCategoryModeratingGroupsChange(groupIds) {
|
||||||
|
this.set("category.moderating_group_ids", groupIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -761,7 +761,7 @@ export default class Category extends RestModel {
|
||||||
"navigate_to_first_post_after_read"
|
"navigate_to_first_post_after_read"
|
||||||
),
|
),
|
||||||
search_priority: this.search_priority,
|
search_priority: this.search_priority,
|
||||||
reviewable_by_group_name: this.reviewable_by_group_name,
|
moderating_group_ids: this.moderating_group_ids,
|
||||||
read_only_banner: this.read_only_banner,
|
read_only_banner: this.read_only_banner,
|
||||||
default_list_filter: this.default_list_filter,
|
default_list_filter: this.default_list_filter,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -751,8 +751,8 @@ export default class Topic extends RestModel {
|
||||||
if (
|
if (
|
||||||
opts.force_destroy ||
|
opts.force_destroy ||
|
||||||
(!deleted_by.staff &&
|
(!deleted_by.staff &&
|
||||||
!deleted_by.groups.some(
|
!deleted_by.groups.some((group) =>
|
||||||
(group) => group.name === this.category?.reviewable_by_group_name
|
this.category?.moderating_group_ids?.includes(group.id)
|
||||||
) &&
|
) &&
|
||||||
!deleted_by.can_delete_all_posts_and_topics)
|
!deleted_by.can_delete_all_posts_and_topics)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -578,10 +578,9 @@ class CategoriesController < ApplicationController
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
conditional_param_keys = []
|
||||||
if SiteSetting.enable_category_group_moderation?
|
if SiteSetting.enable_category_group_moderation?
|
||||||
params[:reviewable_by_group_id] = Group.where(
|
conditional_param_keys << { moderating_group_ids: [] }
|
||||||
name: params[:reviewable_by_group_name],
|
|
||||||
).pick(:id) if params[:reviewable_by_group_name]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result =
|
result =
|
||||||
|
@ -621,7 +620,7 @@ class CategoriesController < ApplicationController
|
||||||
:allow_global_tags,
|
:allow_global_tags,
|
||||||
:read_only_banner,
|
:read_only_banner,
|
||||||
:default_list_filter,
|
:default_list_filter,
|
||||||
:reviewable_by_group_id,
|
*conditional_param_keys,
|
||||||
category_setting_attributes: %i[
|
category_setting_attributes: %i[
|
||||||
auto_bump_cooldown_days
|
auto_bump_cooldown_days
|
||||||
num_auto_bump_daily
|
num_auto_bump_daily
|
||||||
|
|
|
@ -36,9 +36,8 @@ class ReviewableClaimedTopicsController < ApplicationController
|
||||||
def notify_users(topic, claimed_by)
|
def notify_users(topic, claimed_by)
|
||||||
group_ids = Set.new([Group::AUTO_GROUPS[:staff]])
|
group_ids = Set.new([Group::AUTO_GROUPS[:staff]])
|
||||||
|
|
||||||
if SiteSetting.enable_category_group_moderation? &&
|
if SiteSetting.enable_category_group_moderation? && topic.category
|
||||||
group_id = topic.category&.reviewable_by_group_id.presence
|
group_ids.merge(topic.category.moderating_group_ids)
|
||||||
group_ids.add(group_id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if claimed_by.present?
|
if claimed_by.present?
|
||||||
|
|
|
@ -20,23 +20,18 @@ class Jobs::NotifyReviewable < ::Jobs::Base
|
||||||
|
|
||||||
all_updates[:admins][r.id] = payload
|
all_updates[:admins][r.id] = payload
|
||||||
all_updates[:moderators][r.id] = payload if r.reviewable_by_moderator?
|
all_updates[:moderators][r.id] = payload if r.reviewable_by_moderator?
|
||||||
all_updates[r.reviewable_by_group_id][r.id] = payload if r.reviewable_by_group_id
|
|
||||||
|
if SiteSetting.enable_category_group_moderation? && r.category.present?
|
||||||
|
r
|
||||||
|
.category
|
||||||
|
.moderating_groups
|
||||||
|
.pluck(:id)
|
||||||
|
.each { |group_id| all_updates[group_id][r.id] = payload }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
DistributedMutex.synchronize("notify_reviewable_job", validity: 120) do
|
DistributedMutex.synchronize("notify_reviewable_job", validity: 120) do
|
||||||
counts = Hash.new(0)
|
|
||||||
Reviewable
|
|
||||||
.default_visible
|
|
||||||
.pending
|
|
||||||
.group(:reviewable_by_moderator, :reviewable_by_group_id)
|
|
||||||
.pluck(:reviewable_by_moderator, :reviewable_by_group_id, "count(*)")
|
|
||||||
.each do |reviewable_by_moderator, reviewable_by_group_id, count|
|
|
||||||
counts[:admins] += count
|
|
||||||
counts[:moderators] += count if reviewable_by_moderator
|
|
||||||
counts[reviewable_by_group_id] += count if reviewable_by_group_id
|
|
||||||
end
|
|
||||||
|
|
||||||
notify_users(User.real.admins, all_updates[:admins])
|
notify_users(User.real.admins, all_updates[:admins])
|
||||||
|
|
||||||
if reviewable.reviewable_by_moderator?
|
if reviewable.reviewable_by_moderator?
|
||||||
|
@ -46,16 +41,21 @@ class Jobs::NotifyReviewable < ::Jobs::Base
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
if SiteSetting.enable_category_group_moderation? && (group = reviewable.reviewable_by_group)
|
if SiteSetting.enable_category_group_moderation? && reviewable.category.present?
|
||||||
users = group.users.includes(:group_users).where("users.id NOT IN (?)", @contacted)
|
users =
|
||||||
|
User
|
||||||
|
.includes(:group_users)
|
||||||
|
.joins(:group_users)
|
||||||
|
.joins(
|
||||||
|
"INNER JOIN category_moderation_groups ON category_moderation_groups.group_id = group_users.group_id",
|
||||||
|
)
|
||||||
|
.where("category_moderation_groups.category_id": reviewable.category.id)
|
||||||
|
.where("users.id NOT IN (?)", @contacted)
|
||||||
|
.distinct
|
||||||
|
|
||||||
users.find_each do |user|
|
users.find_each do |user|
|
||||||
count = 0
|
|
||||||
updates = {}
|
updates = {}
|
||||||
user.group_users.each do |gu|
|
user.group_users.each { |gu| updates.merge!(all_updates[gu.group_id]) }
|
||||||
updates.merge!(all_updates[gu.group_id])
|
|
||||||
count += counts[gu.group_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
notify_user(user, updates)
|
notify_user(user, updates)
|
||||||
end
|
end
|
||||||
|
|
|
@ -88,7 +88,7 @@ class About
|
||||||
allowed_cats = Guardian.new(@user).allowed_category_ids
|
allowed_cats = Guardian.new(@user).allowed_category_ids
|
||||||
return [] if allowed_cats.blank?
|
return [] if allowed_cats.blank?
|
||||||
|
|
||||||
cats_with_mods = Category.where.not(reviewable_by_group_id: nil).pluck(:id)
|
cats_with_mods = Category.joins(:category_moderation_groups).distinct.pluck(:id)
|
||||||
|
|
||||||
category_ids = cats_with_mods & allowed_cats
|
category_ids = cats_with_mods & allowed_cats
|
||||||
return [] if category_ids.blank?
|
return [] if category_ids.blank?
|
||||||
|
@ -96,15 +96,32 @@ class About
|
||||||
per_cat_limit = category_mods_limit / category_ids.size
|
per_cat_limit = category_mods_limit / category_ids.size
|
||||||
per_cat_limit = 1 if per_cat_limit < 1
|
per_cat_limit = 1 if per_cat_limit < 1
|
||||||
|
|
||||||
results = DB.query(<<~SQL, category_ids: category_ids)
|
results = DB.query(<<~SQL, category_ids:)
|
||||||
SELECT c.id category_id
|
WITH moderator_users AS (
|
||||||
, (ARRAY_AGG(u.id ORDER BY u.last_seen_at DESC))[:#{per_cat_limit}] user_ids
|
SELECT
|
||||||
FROM categories c
|
cmg.category_id AS category_id,
|
||||||
JOIN group_users gu ON gu.group_id = c.reviewable_by_group_id
|
u.id AS user_id,
|
||||||
JOIN users u ON u.id = gu.user_id
|
u.last_seen_at,
|
||||||
WHERE c.id IN (:category_ids)
|
ROW_NUMBER() OVER (PARTITION BY cmg.category_id, u.id ORDER BY u.last_seen_at DESC) as rn
|
||||||
GROUP BY c.id
|
FROM category_moderation_groups cmg
|
||||||
ORDER BY c.position
|
INNER JOIN group_users gu
|
||||||
|
ON cmg.group_id = gu.group_id
|
||||||
|
INNER JOIN users u
|
||||||
|
ON gu.user_id = u.id
|
||||||
|
WHERE cmg.category_id IN (:category_ids)
|
||||||
|
)
|
||||||
|
SELECT id AS category_id, user_ids
|
||||||
|
FROM categories
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT
|
||||||
|
category_id,
|
||||||
|
(ARRAY_AGG(user_id ORDER BY last_seen_at DESC))[:#{per_cat_limit}] AS user_ids
|
||||||
|
FROM moderator_users
|
||||||
|
WHERE rn = 1
|
||||||
|
GROUP BY category_id
|
||||||
|
) X
|
||||||
|
ON X.category_id = id
|
||||||
|
ORDER BY position
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
cats = Category.where(id: results.map(&:category_id)).index_by(&:id)
|
cats = Category.where(id: results.map(&:category_id)).index_by(&:id)
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Category < ActiveRecord::Base
|
||||||
:suppress_from_latest, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
:suppress_from_latest, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
||||||
:required_tag_group_id, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
:required_tag_group_id, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
||||||
:min_tags_from_required_group, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
:min_tags_from_required_group, # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
|
||||||
|
:reviewable_by_group_id,
|
||||||
]
|
]
|
||||||
|
|
||||||
include Searchable
|
include Searchable
|
||||||
|
@ -37,7 +38,9 @@ class Category < ActiveRecord::Base
|
||||||
has_many :featured_topics, through: :category_featured_topics, source: :topic
|
has_many :featured_topics, through: :category_featured_topics, source: :topic
|
||||||
|
|
||||||
has_many :category_groups, dependent: :destroy
|
has_many :category_groups, dependent: :destroy
|
||||||
|
has_many :category_moderation_groups, dependent: :destroy
|
||||||
has_many :groups, through: :category_groups
|
has_many :groups, through: :category_groups
|
||||||
|
has_many :moderating_groups, through: :category_moderation_groups, source: :group
|
||||||
has_many :topic_timers, dependent: :destroy
|
has_many :topic_timers, dependent: :destroy
|
||||||
has_many :upload_references, as: :target, dependent: :destroy
|
has_many :upload_references, as: :target, dependent: :destroy
|
||||||
|
|
||||||
|
@ -110,7 +113,6 @@ class Category < ActiveRecord::Base
|
||||||
after_save :reset_topic_ids_cache
|
after_save :reset_topic_ids_cache
|
||||||
after_save :clear_subcategory_ids
|
after_save :clear_subcategory_ids
|
||||||
after_save :clear_url_cache
|
after_save :clear_url_cache
|
||||||
after_save :update_reviewables
|
|
||||||
after_save :publish_discourse_stylesheet
|
after_save :publish_discourse_stylesheet
|
||||||
after_save :publish_category
|
after_save :publish_category
|
||||||
|
|
||||||
|
@ -163,8 +165,6 @@ class Category < ActiveRecord::Base
|
||||||
has_many :category_form_templates, dependent: :destroy
|
has_many :category_form_templates, dependent: :destroy
|
||||||
has_many :form_templates, through: :category_form_templates
|
has_many :form_templates, through: :category_form_templates
|
||||||
|
|
||||||
belongs_to :reviewable_by_group, class_name: "Group"
|
|
||||||
|
|
||||||
scope :latest, -> { order("topic_count DESC") }
|
scope :latest, -> { order("topic_count DESC") }
|
||||||
|
|
||||||
scope :secured,
|
scope :secured,
|
||||||
|
@ -1113,10 +1113,8 @@ class Category < ActiveRecord::Base
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_reviewables
|
def moderating_group_ids
|
||||||
if should_update_reviewables?
|
category_moderation_groups.pluck(:group_id)
|
||||||
Reviewable.where(category_id: id).update_all(reviewable_by_group_id: reviewable_by_group_id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_by_slug_path(slug_path)
|
def self.find_by_slug_path(slug_path)
|
||||||
|
@ -1278,10 +1276,6 @@ class Category < ActiveRecord::Base
|
||||||
self.build_category_setting if self.category_setting.blank?
|
self.build_category_setting if self.category_setting.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_update_reviewables?
|
|
||||||
SiteSetting.enable_category_group_moderation? && saved_change_to_reviewable_by_group_id?
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_permissions_compatibility(parent_permissions, child_permissions)
|
def check_permissions_compatibility(parent_permissions, child_permissions)
|
||||||
parent_groups = parent_permissions.map(&:first)
|
parent_groups = parent_permissions.map(&:first)
|
||||||
|
|
||||||
|
@ -1381,7 +1375,6 @@ end
|
||||||
# navigate_to_first_post_after_read :boolean default(FALSE), not null
|
# navigate_to_first_post_after_read :boolean default(FALSE), not null
|
||||||
# search_priority :integer default(0)
|
# search_priority :integer default(0)
|
||||||
# allow_global_tags :boolean default(FALSE), not null
|
# allow_global_tags :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# read_only_banner :string
|
# read_only_banner :string
|
||||||
# default_list_filter :string(20) default("all")
|
# default_list_filter :string(20) default("all")
|
||||||
# allow_unlimited_owner_edits_on_first_post :boolean default(FALSE), not null
|
# allow_unlimited_owner_edits_on_first_post :boolean default(FALSE), not null
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CategoryModerationGroup < ActiveRecord::Base
|
||||||
|
belongs_to :category
|
||||||
|
belongs_to :group
|
||||||
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: category_moderation_groups
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# category_id :integer
|
||||||
|
# group_id :integer
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_category_moderation_groups_on_category_id_and_group_id (category_id,group_id) UNIQUE
|
||||||
|
#
|
|
@ -16,6 +16,7 @@ class Group < ActiveRecord::Base
|
||||||
self.preloaded_custom_field_names = Set.new
|
self.preloaded_custom_field_names = Set.new
|
||||||
|
|
||||||
has_many :category_groups, dependent: :destroy
|
has_many :category_groups, dependent: :destroy
|
||||||
|
has_many :category_moderation_groups, dependent: :destroy
|
||||||
has_many :group_users, dependent: :destroy
|
has_many :group_users, dependent: :destroy
|
||||||
has_many :group_requests, dependent: :destroy
|
has_many :group_requests, dependent: :destroy
|
||||||
has_many :group_mentions, dependent: :destroy
|
has_many :group_mentions, dependent: :destroy
|
||||||
|
@ -24,15 +25,11 @@ class Group < ActiveRecord::Base
|
||||||
has_many :group_archived_messages, dependent: :destroy
|
has_many :group_archived_messages, dependent: :destroy
|
||||||
|
|
||||||
has_many :categories, through: :category_groups
|
has_many :categories, through: :category_groups
|
||||||
|
has_many :moderation_categories, through: :category_moderation_groups, source: :category
|
||||||
has_many :users, through: :group_users
|
has_many :users, through: :group_users
|
||||||
has_many :human_users, -> { human_users }, through: :group_users, source: :user
|
has_many :human_users, -> { human_users }, through: :group_users, source: :user
|
||||||
has_many :requesters, through: :group_requests, source: :user
|
has_many :requesters, through: :group_requests, source: :user
|
||||||
has_many :group_histories, dependent: :destroy
|
has_many :group_histories, dependent: :destroy
|
||||||
has_many :category_reviews,
|
|
||||||
class_name: "Category",
|
|
||||||
foreign_key: :reviewable_by_group_id,
|
|
||||||
dependent: :nullify
|
|
||||||
has_many :reviewables, foreign_key: :reviewable_by_group_id, dependent: :nullify
|
|
||||||
has_many :group_category_notification_defaults, dependent: :destroy
|
has_many :group_category_notification_defaults, dependent: :destroy
|
||||||
has_many :group_tag_notification_defaults, dependent: :destroy
|
has_many :group_tag_notification_defaults, dependent: :destroy
|
||||||
has_many :associated_groups, through: :group_associated_groups, dependent: :destroy
|
has_many :associated_groups, through: :group_associated_groups, dependent: :destroy
|
||||||
|
@ -81,10 +78,6 @@ class Group < ActiveRecord::Base
|
||||||
Discourse.cache.delete("group_imap_mailboxes_#{self.id}")
|
Discourse.cache.delete("group_imap_mailboxes_#{self.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_review_groups
|
|
||||||
Category.where(review_group_id: self.id).update_all(review_group_id: nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
validate :name_format_validator
|
validate :name_format_validator
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validate :automatic_membership_email_domains_format_validator
|
validate :automatic_membership_email_domains_format_validator
|
||||||
|
|
|
@ -7,6 +7,8 @@ class Reviewable < ActiveRecord::Base
|
||||||
ReviewableUser: BasicReviewableUserSerializer,
|
ReviewableUser: BasicReviewableUserSerializer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ignored_columns = [:reviewable_by_group_id]
|
||||||
|
|
||||||
class UpdateConflict < StandardError
|
class UpdateConflict < StandardError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,13 +19,11 @@ class Reviewable < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
before_save :apply_review_group
|
|
||||||
attr_accessor :created_new
|
attr_accessor :created_new
|
||||||
validates_presence_of :type, :status, :created_by_id
|
validates_presence_of :type, :status, :created_by_id
|
||||||
belongs_to :target, polymorphic: true
|
belongs_to :target, polymorphic: true
|
||||||
belongs_to :created_by, class_name: "User"
|
belongs_to :created_by, class_name: "User"
|
||||||
belongs_to :target_created_by, class_name: "User"
|
belongs_to :target_created_by, class_name: "User"
|
||||||
belongs_to :reviewable_by_group, class_name: "Group"
|
|
||||||
|
|
||||||
# Optional, for filtering
|
# Optional, for filtering
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
|
@ -264,15 +264,6 @@ class Reviewable < ActiveRecord::Base
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_review_group
|
|
||||||
unless SiteSetting.enable_category_group_moderation? && category.present? &&
|
|
||||||
category.reviewable_by_group_id
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.reviewable_by_group_id = category.reviewable_by_group_id
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_for(guardian, args = nil)
|
def actions_for(guardian, args = nil)
|
||||||
args ||= {}
|
args ||= {}
|
||||||
|
|
||||||
|
@ -408,14 +399,17 @@ class Reviewable < ActiveRecord::Base
|
||||||
group_ids =
|
group_ids =
|
||||||
SiteSetting.enable_category_group_moderation? ? user.group_users.pluck(:group_id) : []
|
SiteSetting.enable_category_group_moderation? ? user.group_users.pluck(:group_id) : []
|
||||||
|
|
||||||
result.where(
|
result
|
||||||
"(reviewables.reviewable_by_moderator AND :staff) OR (reviewables.reviewable_by_group_id IN (:group_ids))",
|
.left_joins(category: :category_moderation_groups)
|
||||||
staff: user.staff?,
|
.where(
|
||||||
group_ids: group_ids,
|
"(reviewables.reviewable_by_moderator AND :moderator) OR (category_moderation_groups.group_id IN (:group_ids))",
|
||||||
).where(
|
moderator: user.moderator?,
|
||||||
"reviewables.category_id IS NULL OR reviewables.category_id IN (?)",
|
group_ids: group_ids,
|
||||||
Guardian.new(user).allowed_category_ids,
|
)
|
||||||
)
|
.where(
|
||||||
|
"reviewables.category_id IS NULL OR reviewables.category_id IN (?)",
|
||||||
|
Guardian.new(user).allowed_category_ids,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pending_count(user)
|
def self.pending_count(user)
|
||||||
|
@ -768,7 +762,6 @@ end
|
||||||
# status :integer default("pending"), not null
|
# status :integer default("pending"), not null
|
||||||
# created_by_id :integer not null
|
# created_by_id :integer not null
|
||||||
# reviewable_by_moderator :boolean default(FALSE), not null
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# category_id :integer
|
# category_id :integer
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# score :float default(0.0), not null
|
# score :float default(0.0), not null
|
||||||
|
|
|
@ -337,9 +337,16 @@ class ReviewableFlaggedPost < Reviewable
|
||||||
|
|
||||||
user_ids = User.staff.pluck(:id)
|
user_ids = User.staff.pluck(:id)
|
||||||
|
|
||||||
if SiteSetting.enable_category_group_moderation? &&
|
if SiteSetting.enable_category_group_moderation? && topic.category
|
||||||
group_id = topic.category&.reviewable_by_group_id.presence
|
user_ids.concat(
|
||||||
user_ids.concat(GroupUser.where(group_id: group_id).pluck(:user_id))
|
GroupUser
|
||||||
|
.joins(
|
||||||
|
"INNER JOIN category_moderation_groups ON category_moderation_groups.group_id = group_users.group_id",
|
||||||
|
)
|
||||||
|
.where("category_moderation_groups.category_id": topic.category.id)
|
||||||
|
.distinct
|
||||||
|
.pluck(:user_id),
|
||||||
|
)
|
||||||
user_ids.uniq!
|
user_ids.uniq!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -388,7 +395,6 @@ end
|
||||||
# status :integer default("pending"), not null
|
# status :integer default("pending"), not null
|
||||||
# created_by_id :integer not null
|
# created_by_id :integer not null
|
||||||
# reviewable_by_moderator :boolean default(FALSE), not null
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# category_id :integer
|
# category_id :integer
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# score :float default(0.0), not null
|
# score :float default(0.0), not null
|
||||||
|
|
|
@ -141,7 +141,6 @@ end
|
||||||
# status :integer default("pending"), not null
|
# status :integer default("pending"), not null
|
||||||
# created_by_id :integer not null
|
# created_by_id :integer not null
|
||||||
# reviewable_by_moderator :boolean default(FALSE), not null
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# category_id :integer
|
# category_id :integer
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# score :float default(0.0), not null
|
# score :float default(0.0), not null
|
||||||
|
|
|
@ -241,7 +241,6 @@ end
|
||||||
# status :integer default("pending"), not null
|
# status :integer default("pending"), not null
|
||||||
# created_by_id :integer not null
|
# created_by_id :integer not null
|
||||||
# reviewable_by_moderator :boolean default(FALSE), not null
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# category_id :integer
|
# category_id :integer
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# score :float default(0.0), not null
|
# score :float default(0.0), not null
|
||||||
|
|
|
@ -105,7 +105,6 @@ end
|
||||||
# status :integer default("pending"), not null
|
# status :integer default("pending"), not null
|
||||||
# created_by_id :integer not null
|
# created_by_id :integer not null
|
||||||
# reviewable_by_moderator :boolean default(FALSE), not null
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
||||||
# reviewable_by_group_id :integer
|
|
||||||
# category_id :integer
|
# category_id :integer
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# score :float default(0.0), not null
|
# score :float default(0.0), not null
|
||||||
|
|
|
@ -26,17 +26,13 @@ class CategorySerializer < SiteCategorySerializer
|
||||||
:custom_fields,
|
:custom_fields,
|
||||||
:topic_featured_link_allowed,
|
:topic_featured_link_allowed,
|
||||||
:search_priority,
|
:search_priority,
|
||||||
:reviewable_by_group_name,
|
:moderating_group_ids,
|
||||||
:default_slow_mode_seconds
|
:default_slow_mode_seconds
|
||||||
|
|
||||||
has_one :category_setting, serializer: CategorySettingSerializer, embed: :objects
|
has_one :category_setting, serializer: CategorySettingSerializer, embed: :objects
|
||||||
|
|
||||||
def reviewable_by_group_name
|
def include_moderating_group_ids?
|
||||||
object.reviewable_by_group.name
|
SiteSetting.enable_category_group_moderation?
|
||||||
end
|
|
||||||
|
|
||||||
def include_reviewable_by_group_name?
|
|
||||||
SiteSetting.enable_category_group_moderation? && object.reviewable_by_group_id.present?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_category_setting?
|
def include_category_setting?
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateCategoryModerationGroups < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :category_moderation_groups do |t|
|
||||||
|
t.integer :category_id
|
||||||
|
t.integer :group_id
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :category_moderation_groups, %i[category_id group_id], unique: true
|
||||||
|
|
||||||
|
execute <<~SQL
|
||||||
|
INSERT INTO category_moderation_groups (category_id, group_id, created_at, updated_at)
|
||||||
|
SELECT id, reviewable_by_group_id, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
||||||
|
FROM categories
|
||||||
|
WHERE reviewable_by_group_id IS NOT NULL
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
end
|
|
@ -136,17 +136,14 @@ class Guardian
|
||||||
return false if !category
|
return false if !category
|
||||||
return false if !category_group_moderation_allowed?
|
return false if !category_group_moderation_allowed?
|
||||||
|
|
||||||
reviewable_by_group_id = category.reviewable_by_group_id
|
@group_moderator_categories ||= {}
|
||||||
return false if reviewable_by_group_id.blank?
|
|
||||||
|
|
||||||
@category_group_moderator_groups ||= {}
|
if @group_moderator_categories.key?(category.id)
|
||||||
|
@group_moderator_categories[category.id]
|
||||||
if @category_group_moderator_groups.key?(reviewable_by_group_id)
|
|
||||||
@category_group_moderator_groups[reviewable_by_group_id]
|
|
||||||
else
|
else
|
||||||
@category_group_moderator_groups[
|
@group_moderator_categories[category.id] = category_group_moderator_scope.exists?(
|
||||||
reviewable_by_group_id
|
id: category.id,
|
||||||
] = category_group_moderator_scope.exists?("categories.id": category.id)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -692,8 +689,9 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def category_group_moderator_scope
|
def category_group_moderator_scope
|
||||||
Category.joins(
|
Category
|
||||||
"INNER JOIN group_users ON group_users.group_id = categories.reviewable_by_group_id",
|
.joins(:category_moderation_groups)
|
||||||
).where("group_users.user_id = ?", user.id)
|
.joins("INNER JOIN group_users ON group_users.group_id = category_moderation_groups.group_id")
|
||||||
|
.where("group_users.user_id": user.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,8 +171,13 @@ module UserGuardian
|
||||||
(
|
(
|
||||||
SiteSetting.enable_category_group_moderation &&
|
SiteSetting.enable_category_group_moderation &&
|
||||||
Reviewable
|
Reviewable
|
||||||
.where(reviewable_by_group_id: @user.group_users.pluck(:group_id))
|
.joins(
|
||||||
.where("category_id IS NULL or category_id IN (?)", allowed_category_ids)
|
"INNER JOIN category_moderation_groups ON category_moderation_groups.category_id = reviewables.category_id",
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
category_id: allowed_category_ids,
|
||||||
|
"category_moderation_groups.group_id": @user.group_users.pluck(:group_id),
|
||||||
|
)
|
||||||
.exists?
|
.exists?
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -364,9 +364,13 @@ class PostActionCreator
|
||||||
create_args[:subtype] = TopicSubtype.notify_moderators
|
create_args[:subtype] = TopicSubtype.notify_moderators
|
||||||
create_args[:target_group_names] = [Group[:moderators].name]
|
create_args[:target_group_names] = [Group[:moderators].name]
|
||||||
|
|
||||||
if SiteSetting.enable_category_group_moderation? &&
|
if SiteSetting.enable_category_group_moderation? && @post.topic&.category
|
||||||
@post.topic&.category&.reviewable_by_group_id?
|
create_args[:target_group_names].push(
|
||||||
create_args[:target_group_names] << @post.topic.category.reviewable_by_group.name
|
*Group
|
||||||
|
.joins(:category_moderation_groups)
|
||||||
|
.where("category_moderation_groups.category_id": @post.topic.category.id)
|
||||||
|
.pluck(:name),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -535,16 +535,19 @@ class TopicView
|
||||||
def category_group_moderator_user_ids
|
def category_group_moderator_user_ids
|
||||||
@category_group_moderator_user_ids ||=
|
@category_group_moderator_user_ids ||=
|
||||||
begin
|
begin
|
||||||
if SiteSetting.enable_category_group_moderation? &&
|
if SiteSetting.enable_category_group_moderation? && @topic.category.present?
|
||||||
@topic.category&.reviewable_by_group.present?
|
|
||||||
posts_user_ids = Set.new(@posts.map(&:user_id))
|
posts_user_ids = Set.new(@posts.map(&:user_id))
|
||||||
Set.new(
|
Set.new(
|
||||||
@topic
|
GroupUser
|
||||||
.category
|
.joins(
|
||||||
.reviewable_by_group
|
"INNER JOIN category_moderation_groups ON category_moderation_groups.group_id = group_users.group_id",
|
||||||
.group_users
|
)
|
||||||
.where(user_id: posts_user_ids)
|
.where(
|
||||||
.pluck("distinct user_id"),
|
"category_moderation_groups.category_id": @topic.category.id,
|
||||||
|
user_id: posts_user_ids,
|
||||||
|
)
|
||||||
|
.distinct
|
||||||
|
.pluck(:user_id),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Set.new
|
Set.new
|
||||||
|
|
|
@ -354,11 +354,11 @@ RSpec.describe Chat::GuardianExtensions do
|
||||||
context "when enable_category_group_moderation is true" do
|
context "when enable_category_group_moderation is true" do
|
||||||
before { SiteSetting.enable_category_group_moderation = true }
|
before { SiteSetting.enable_category_group_moderation = true }
|
||||||
|
|
||||||
it "returns true if the regular user is part of the reviewable_by_group for the category" do
|
it "returns true if the regular user is part of the reviewable groups for the category" do
|
||||||
moderator = Fabricate(:user)
|
moderator = Fabricate(:user)
|
||||||
mods = Fabricate(:group)
|
mods = Fabricate(:group)
|
||||||
mods.add(moderator)
|
mods.add(moderator)
|
||||||
category.update!(reviewable_by_group: mods)
|
Fabricate(:category_moderation_group, category:, group: mods)
|
||||||
expect(Guardian.new(Fabricate(:admin)).can_moderate_chat?(channel.chatable)).to eq(true)
|
expect(Guardian.new(Fabricate(:admin)).can_moderate_chat?(channel.chatable)).to eq(true)
|
||||||
expect(Guardian.new(moderator).can_moderate_chat?(channel.chatable)).to eq(true)
|
expect(Guardian.new(moderator).can_moderate_chat?(channel.chatable)).to eq(true)
|
||||||
end
|
end
|
||||||
|
@ -443,7 +443,7 @@ RSpec.describe Chat::GuardianExtensions do
|
||||||
moderator = Fabricate(:user)
|
moderator = Fabricate(:user)
|
||||||
mods = Fabricate(:group)
|
mods = Fabricate(:group)
|
||||||
mods.add(moderator)
|
mods.add(moderator)
|
||||||
chatable.update!(reviewable_by_group: mods)
|
Fabricate(:category_moderation_group, category: chatable, group: mods)
|
||||||
expect(Guardian.new(moderator).can_restore_chat?(message, chatable)).to eq(true)
|
expect(Guardian.new(moderator).can_restore_chat?(message, chatable)).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,12 +28,14 @@ RSpec.describe "Move message to channel", type: :system do
|
||||||
fab!(:group_1) { Fabricate(:group) }
|
fab!(:group_1) { Fabricate(:group) }
|
||||||
fab!(:channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
fab!(:channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
||||||
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1, user: current_user) }
|
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1, user: current_user) }
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category: channel_1.category, group: group_1)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
group_1.add(current_user)
|
group_1.add(current_user)
|
||||||
channel_1.add(current_user)
|
channel_1.add(current_user)
|
||||||
channel_1.chatable.update!(reviewable_by_group_id: group_1.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is available" do
|
it "is available" do
|
||||||
|
|
|
@ -59,9 +59,8 @@ after_initialize do
|
||||||
config.allowed_group_ids += SiteSetting.edit_all_post_groups.split("|").map(&:to_i)
|
config.allowed_group_ids += SiteSetting.edit_all_post_groups.split("|").map(&:to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
if SiteSetting.enable_category_group_moderation? &&
|
if SiteSetting.enable_category_group_moderation? && topic.category
|
||||||
group_id = topic.category&.reviewable_by_group_id
|
config.allowed_group_ids.push(*topic.category.moderating_groups.pluck(:id))
|
||||||
config.allowed_group_ids << group_id
|
|
||||||
end
|
end
|
||||||
|
|
||||||
config
|
config
|
||||||
|
|
|
@ -73,7 +73,7 @@ RSpec.describe "discourse-presence" do
|
||||||
expect(c.config.allowed_group_ids).to contain_exactly(Group::AUTO_GROUPS[:staff])
|
expect(c.config.allowed_group_ids).to contain_exactly(Group::AUTO_GROUPS[:staff])
|
||||||
|
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
category.update(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
|
||||||
c = PresenceChannel.new("/discourse-presence/edit/#{p.id}", use_cache: false)
|
c = PresenceChannel.new("/discourse-presence/edit/#{p.id}", use_cache: false)
|
||||||
expect(c.config.allowed_group_ids).to contain_exactly(Group::AUTO_GROUPS[:staff], group.id)
|
expect(c.config.allowed_group_ids).to contain_exactly(Group::AUTO_GROUPS[:staff], group.id)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Fabricator(:category_moderation_group) do
|
||||||
|
category
|
||||||
|
group
|
||||||
|
end
|
|
@ -54,8 +54,9 @@ RSpec.describe Jobs::NotifyReviewable do
|
||||||
moderator.update!(last_seen_reviewable_id: moderator_reviewable.id)
|
moderator.update!(last_seen_reviewable_id: moderator_reviewable.id)
|
||||||
|
|
||||||
# Content for a group
|
# Content for a group
|
||||||
group_reviewable =
|
category = Fabricate(:category)
|
||||||
Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, category:)
|
||||||
|
|
||||||
messages =
|
messages =
|
||||||
MessageBus.track_publish { described_class.new.execute(reviewable_id: group_reviewable.id) }
|
MessageBus.track_publish { described_class.new.execute(reviewable_id: group_reviewable.id) }
|
||||||
|
@ -85,7 +86,9 @@ RSpec.describe Jobs::NotifyReviewable do
|
||||||
SiteSetting.enable_category_group_moderation = false
|
SiteSetting.enable_category_group_moderation = false
|
||||||
|
|
||||||
GroupUser.create!(group_id: group.id, user_id: moderator.id)
|
GroupUser.create!(group_id: group.id, user_id: moderator.id)
|
||||||
reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, reviewable_by_group: group)
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, category:)
|
||||||
|
|
||||||
messages =
|
messages =
|
||||||
MessageBus.track_publish("/reviewable_counts") do
|
MessageBus.track_publish("/reviewable_counts") do
|
||||||
|
|
|
@ -5,20 +5,16 @@ RSpec.describe Jobs::RefreshUsersReviewableCounts do
|
||||||
fab!(:moderator)
|
fab!(:moderator)
|
||||||
fab!(:user)
|
fab!(:user)
|
||||||
|
|
||||||
fab!(:group)
|
fab!(:group) { Fabricate(:group, users: [user]) }
|
||||||
fab!(:topic)
|
fab!(:category)
|
||||||
fab!(:reviewable1) do
|
fab!(:topic) { Fabricate(:topic, category:) }
|
||||||
Fabricate(:reviewable, reviewable_by_group: group, reviewable_by_moderator: true, topic: topic)
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
||||||
end
|
fab!(:reviewable1) { Fabricate(:reviewable, reviewable_by_moderator: true, topic:, category:) }
|
||||||
|
|
||||||
fab!(:reviewable2) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
fab!(:reviewable2) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
||||||
fab!(:reviewable3) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
fab!(:reviewable3) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
||||||
|
|
||||||
before do
|
before { SiteSetting.enable_category_group_moderation = true }
|
||||||
SiteSetting.enable_category_group_moderation = true
|
|
||||||
group.add(user)
|
|
||||||
topic.category.update!(reviewable_by_group: group)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#execute" do
|
describe "#execute" do
|
||||||
it "publishes reviewable counts for the members of the specified groups" do
|
it "publishes reviewable counts for the members of the specified groups" do
|
||||||
|
|
|
@ -83,7 +83,7 @@ RSpec.describe TopicGuardian do
|
||||||
it "returns true for group moderator" do
|
it "returns true for group moderator" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
expect(Guardian.new(user).can_see_deleted_topics?(topic.category)).to eq(false)
|
expect(Guardian.new(user).can_see_deleted_topics?(topic.category)).to eq(false)
|
||||||
category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
group.add(user)
|
group.add(user)
|
||||||
topic.update!(category: category)
|
topic.update!(category: category)
|
||||||
expect(Guardian.new(user).can_see_deleted_topics?(topic.category)).to eq(true)
|
expect(Guardian.new(user).can_see_deleted_topics?(topic.category)).to eq(true)
|
||||||
|
@ -110,7 +110,7 @@ RSpec.describe TopicGuardian do
|
||||||
it "returns true for group moderator" do
|
it "returns true for group moderator" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
expect(Guardian.new(user).can_recover_topic?(Topic.with_deleted.last)).to eq(false)
|
expect(Guardian.new(user).can_recover_topic?(Topic.with_deleted.last)).to eq(false)
|
||||||
category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
group.add(user)
|
group.add(user)
|
||||||
topic.update!(category: category)
|
topic.update!(category: category)
|
||||||
expect(Guardian.new(user).can_recover_topic?(Topic.with_deleted.last)).to eq(true)
|
expect(Guardian.new(user).can_recover_topic?(Topic.with_deleted.last)).to eq(true)
|
||||||
|
@ -255,7 +255,7 @@ RSpec.describe TopicGuardian do
|
||||||
it "returns the topic ids for topics which are deleted but user is a category moderator of" do
|
it "returns the topic ids for topics which are deleted but user is a category moderator of" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
||||||
category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
group.add(user)
|
group.add(user)
|
||||||
topic.update!(category: category)
|
topic.update!(category: category)
|
||||||
topic.trash!(admin)
|
topic.trash!(admin)
|
||||||
|
|
|
@ -415,8 +415,10 @@ RSpec.describe UserGuardian do
|
||||||
group.add(user)
|
group.add(user)
|
||||||
guardian = Guardian.new(user)
|
guardian = Guardian.new(user)
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
|
||||||
Fabricate(:reviewable_flagged_post, reviewable_by_group: group, category: nil)
|
Fabricate(:reviewable_flagged_post, category:)
|
||||||
|
|
||||||
expect(guardian.can_see_review_queue?).to eq(true)
|
expect(guardian.can_see_review_queue?).to eq(true)
|
||||||
end
|
end
|
||||||
|
@ -426,8 +428,10 @@ RSpec.describe UserGuardian do
|
||||||
group.add(user)
|
group.add(user)
|
||||||
guardian = Guardian.new(user)
|
guardian = Guardian.new(user)
|
||||||
SiteSetting.enable_category_group_moderation = false
|
SiteSetting.enable_category_group_moderation = false
|
||||||
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
|
||||||
Fabricate(:reviewable_flagged_post, reviewable_by_group: group, category: nil)
|
Fabricate(:reviewable_flagged_post, category:)
|
||||||
|
|
||||||
expect(guardian.can_see_review_queue?).to eq(false)
|
expect(guardian.can_see_review_queue?).to eq(false)
|
||||||
end
|
end
|
||||||
|
@ -438,8 +442,9 @@ RSpec.describe UserGuardian do
|
||||||
guardian = Guardian.new(user)
|
guardian = Guardian.new(user)
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
category = Fabricate(:category, read_restricted: true)
|
category = Fabricate(:category, read_restricted: true)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
|
||||||
Fabricate(:reviewable_flagged_post, reviewable_by_group: group, category: category)
|
Fabricate(:reviewable_flagged_post, category: category)
|
||||||
|
|
||||||
expect(guardian.can_see_review_queue?).to eq(false)
|
expect(guardian.can_see_review_queue?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1072,7 +1072,8 @@ RSpec.describe Guardian do
|
||||||
|
|
||||||
expect(Guardian.new(user_gm).can_see?(topic)).to be_falsey
|
expect(Guardian.new(user_gm).can_see?(topic)).to be_falsey
|
||||||
|
|
||||||
topic.category.update!(reviewable_by_group_id: group.id, topic_id: post.topic.id)
|
topic.category.update!(topic_id: post.topic.id)
|
||||||
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
|
|
||||||
expect(Guardian.new(user_gm).can_see?(topic)).to be_truthy
|
expect(Guardian.new(user_gm).can_see?(topic)).to be_truthy
|
||||||
end
|
end
|
||||||
|
@ -1130,7 +1131,8 @@ RSpec.describe Guardian do
|
||||||
|
|
||||||
expect(Guardian.new(user_gm).can_see?(post)).to be_falsey
|
expect(Guardian.new(user_gm).can_see?(post)).to be_falsey
|
||||||
|
|
||||||
post.topic.category.update!(reviewable_by_group_id: group.id, topic_id: post.topic.id)
|
post.topic.category.update!(topic_id: post.topic.id)
|
||||||
|
Fabricate(:category_moderation_group, category: post.topic.category, group:)
|
||||||
expect(Guardian.new(user_gm).can_see?(post)).to be_truthy
|
expect(Guardian.new(user_gm).can_see?(post)).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1504,7 +1506,7 @@ RSpec.describe Guardian do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns true if user is a member of the appropriate group" do
|
it "returns true if user is a member of the appropriate group" do
|
||||||
topic.category.update!(reviewable_by_group_id: group_user.group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group: group_user.group)
|
||||||
|
|
||||||
expect(Guardian.new(group_user.user).can_recover_topic?(topic)).to be_truthy
|
expect(Guardian.new(group_user.user).can_recover_topic?(topic)).to be_truthy
|
||||||
end
|
end
|
||||||
|
@ -1787,7 +1789,7 @@ RSpec.describe Guardian do
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create!(group_id: group.id, user_id: cat_mod_user.id)
|
GroupUser.create!(group_id: group.id, user_id: cat_mod_user.id)
|
||||||
post.topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: post.topic.category, group:)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns true as a category group moderator user" do
|
it "returns true as a category group moderator user" do
|
||||||
|
@ -2161,7 +2163,7 @@ RSpec.describe Guardian do
|
||||||
it "returns true for a group member with reviewable status" do
|
it "returns true for a group member with reviewable status" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||||
topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
expect(Guardian.new(user).can_review_topic?(topic)).to eq(true)
|
expect(Guardian.new(user).can_review_topic?(topic)).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2182,7 +2184,7 @@ RSpec.describe Guardian do
|
||||||
it "returns true for a group member with reviewable status" do
|
it "returns true for a group member with reviewable status" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||||
topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
expect(Guardian.new(user).can_close_topic?(topic)).to eq(true)
|
expect(Guardian.new(user).can_close_topic?(topic)).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2203,7 +2205,7 @@ RSpec.describe Guardian do
|
||||||
it "returns true for a group member with reviewable status" do
|
it "returns true for a group member with reviewable status" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||||
topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
expect(Guardian.new(user).can_archive_topic?(topic)).to eq(true)
|
expect(Guardian.new(user).can_archive_topic?(topic)).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2224,7 +2226,7 @@ RSpec.describe Guardian do
|
||||||
it "returns true for a group member with reviewable status" do
|
it "returns true for a group member with reviewable status" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||||
topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
expect(Guardian.new(user).can_edit_staff_notes?(topic)).to eq(true)
|
expect(Guardian.new(user).can_edit_staff_notes?(topic)).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2348,7 +2350,7 @@ RSpec.describe Guardian do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns true if user is a member of the appropriate group" do
|
it "returns true if user is a member of the appropriate group" do
|
||||||
topic.category.update!(reviewable_by_group_id: group_user.group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group: group_user.group)
|
||||||
|
|
||||||
expect(Guardian.new(group_user.user).can_delete?(topic)).to be_truthy
|
expect(Guardian.new(group_user.user).can_delete?(topic)).to be_truthy
|
||||||
end
|
end
|
||||||
|
@ -2411,8 +2413,9 @@ RSpec.describe Guardian do
|
||||||
it "returns true for category moderators" do
|
it "returns true for category moderators" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
GroupUser.create(group: group, user: user)
|
GroupUser.create(group: group, user: user)
|
||||||
category = Fabricate(:category, reviewable_by_group_id: group.id)
|
category = Fabricate(:category)
|
||||||
post.topic.update!(category: category)
|
post.topic.update!(category: category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
|
||||||
expect(Guardian.new(user).can_delete?(post)).to eq(true)
|
expect(Guardian.new(user).can_delete?(post)).to eq(true)
|
||||||
end
|
end
|
||||||
|
@ -4399,7 +4402,7 @@ RSpec.describe Guardian do
|
||||||
|
|
||||||
it "should correctly detect category moderation" do
|
it "should correctly detect category moderation" do
|
||||||
group.add(user)
|
group.add(user)
|
||||||
category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
guardian = Guardian.new(user)
|
guardian = Guardian.new(user)
|
||||||
|
|
||||||
# implementation detail, ensure memoization is good (hence testing twice)
|
# implementation detail, ensure memoization is good (hence testing twice)
|
||||||
|
|
|
@ -490,9 +490,12 @@ RSpec.describe NewPostManager do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when posting in the category requires approval" do
|
context "when posting in the category requires approval" do
|
||||||
let!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
||||||
let!(:review_group) { Fabricate(:group) }
|
fab!(:review_group) { Fabricate(:group) }
|
||||||
let!(:category) { Fabricate(:category, reviewable_by_group_id: review_group.id) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: review_group)
|
||||||
|
end
|
||||||
|
|
||||||
context "when new topics require approval" do
|
context "when new topics require approval" do
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -306,7 +306,8 @@ RSpec.describe PostDestroyer do
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
review_group = Fabricate(:group)
|
review_group = Fabricate(:group)
|
||||||
review_category = Fabricate(:category, reviewable_by_group_id: review_group.id)
|
review_category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category: review_category, group: review_group)
|
||||||
@reply.topic.update!(category: review_category)
|
@reply.topic.update!(category: review_category)
|
||||||
review_group.users << review_user
|
review_group.users << review_user
|
||||||
end
|
end
|
||||||
|
@ -552,7 +553,8 @@ RSpec.describe PostDestroyer do
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
review_group = Fabricate(:group)
|
review_group = Fabricate(:group)
|
||||||
review_category = Fabricate(:category, reviewable_by_group_id: review_group.id)
|
review_category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category: review_category, group: review_group)
|
||||||
post.topic.update!(category: review_category)
|
post.topic.update!(category: review_category)
|
||||||
review_group.users << review_user
|
review_group.users << review_user
|
||||||
end
|
end
|
||||||
|
|
|
@ -1091,8 +1091,9 @@ RSpec.describe PostRevisor do
|
||||||
|
|
||||||
context "when logging group moderator edits" do
|
context "when logging group moderator edits" do
|
||||||
fab!(:group_user)
|
fab!(:group_user)
|
||||||
fab!(:category) do
|
fab!(:category) { Fabricate(:category, topic: topic) }
|
||||||
Fabricate(:category, reviewable_by_group_id: group_user.group.id, topic: topic)
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -308,7 +308,8 @@ RSpec.describe TopicQuery do
|
||||||
group_moderator = Fabricate(:user)
|
group_moderator = Fabricate(:user)
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
group.add(group_moderator)
|
group.add(group_moderator)
|
||||||
category = Fabricate(:category, reviewable_by_group: group)
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
_topic = Fabricate(:topic, category: category, deleted_at: 1.year.ago)
|
_topic = Fabricate(:topic, category: category, deleted_at: 1.year.ago)
|
||||||
|
|
||||||
expect(TopicQuery.new(admin, status: "deleted").list_latest.topics.size).to eq(1)
|
expect(TopicQuery.new(admin, status: "deleted").list_latest.topics.size).to eq(1)
|
||||||
|
|
|
@ -386,7 +386,8 @@ RSpec.describe PostValidator do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
GroupUser.create(group: group, user: user)
|
GroupUser.create(group: group, user: user)
|
||||||
category = Fabricate(:category, reviewable_by_group_id: group.id)
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
topic.update!(category: category)
|
topic.update!(category: category)
|
||||||
|
|
||||||
Post.create!(user: other_user, topic: topic, raw: "post number 1", post_number: 1)
|
Post.create!(user: other_user, topic: topic, raw: "post number 1", post_number: 1)
|
||||||
|
|
|
@ -67,31 +67,28 @@ RSpec.describe About do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#category_moderators" do
|
describe "#category_moderators" do
|
||||||
let(:user) { Fabricate(:user) }
|
fab!(:user)
|
||||||
let(:public_cat_moderator) { Fabricate(:user, last_seen_at: 1.month.ago) }
|
fab!(:public_cat_moderator) { Fabricate(:user, last_seen_at: 1.month.ago) }
|
||||||
let(:private_cat_moderator) { Fabricate(:user, last_seen_at: 2.month.ago) }
|
fab!(:private_cat_moderator) { Fabricate(:user, last_seen_at: 2.month.ago) }
|
||||||
let(:common_moderator) { Fabricate(:user, last_seen_at: 3.month.ago) }
|
fab!(:common_moderator) { Fabricate(:user, last_seen_at: 3.month.ago) }
|
||||||
let(:common_moderator_2) { Fabricate(:user, last_seen_at: 4.month.ago) }
|
fab!(:common_moderator_2) { Fabricate(:user, last_seen_at: 4.month.ago) }
|
||||||
|
|
||||||
let(:public_group) do
|
fab!(:public_group) do
|
||||||
group = Fabricate(:public_group)
|
Fabricate(:public_group, users: [public_cat_moderator, common_moderator, common_moderator_2])
|
||||||
group.add(public_cat_moderator)
|
|
||||||
group.add(common_moderator)
|
|
||||||
group.add(common_moderator_2)
|
|
||||||
group
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:private_group) do
|
fab!(:private_group) do
|
||||||
group = Fabricate(:group)
|
Fabricate(:group, users: [private_cat_moderator, common_moderator, common_moderator_2])
|
||||||
group.add(private_cat_moderator)
|
|
||||||
group.add(common_moderator)
|
|
||||||
group.add(common_moderator_2)
|
|
||||||
group
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:public_cat) { Fabricate(:category, reviewable_by_group: public_group) }
|
fab!(:public_cat) { Fabricate(:category) }
|
||||||
let!(:private_cat) do
|
fab!(:public_category_moderation_group) do
|
||||||
Fabricate(:private_category, group: private_group, reviewable_by_group: private_group)
|
Fabricate(:category_moderation_group, category: public_cat, group: public_group)
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:private_cat) { Fabricate(:private_category, group: private_group) }
|
||||||
|
fab!(:private_category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category: private_cat, group: private_group)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "lists moderators of the category that the current user can see" do
|
it "lists moderators of the category that the current user can see" do
|
||||||
|
@ -120,6 +117,19 @@ RSpec.describe About do
|
||||||
expect(results.size).to eq(2)
|
expect(results.size).to eq(2)
|
||||||
results.each { |res| expect(res.moderators.size).to eq(2) }
|
results.each { |res| expect(res.moderators.size).to eq(2) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "doesn't list the same user twice as a category mod if the user is member of multiple groups" do
|
||||||
|
Fabricate(:category_moderation_group, category: public_cat, group: private_group)
|
||||||
|
|
||||||
|
results = About.new(nil).category_moderators
|
||||||
|
mods = results.find { |r| r.category.id == public_cat.id }.moderators
|
||||||
|
expect(mods.map(&:id)).to contain_exactly(
|
||||||
|
public_cat_moderator.id,
|
||||||
|
common_moderator.id,
|
||||||
|
common_moderator_2.id,
|
||||||
|
private_cat_moderator.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#admins" do
|
describe "#admins" do
|
||||||
|
|
|
@ -82,51 +82,19 @@ RSpec.describe Category do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#review_group_id" do
|
describe "#category_moderation_groups" do
|
||||||
fab!(:group)
|
fab!(:group)
|
||||||
fab!(:category) { Fabricate(:category_with_definition, reviewable_by_group: group) }
|
fab!(:category) { Fabricate(:category_with_definition) }
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:post) { Fabricate(:post, topic: topic) }
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
||||||
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
||||||
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
||||||
|
|
||||||
it "will add the group to the reviewable" do
|
it "is destroyed if the group is destroyed" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
expect(category.category_moderation_groups).to contain_exactly(category_moderation_group)
|
||||||
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
||||||
expect(reviewable.reviewable_by_group_id).to eq(group.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "will add the group to the reviewable even if created manually" do
|
|
||||||
SiteSetting.enable_category_group_moderation = true
|
|
||||||
reviewable =
|
|
||||||
ReviewableFlaggedPost.create!(
|
|
||||||
created_by: user,
|
|
||||||
payload: {
|
|
||||||
raw: "test raw",
|
|
||||||
},
|
|
||||||
category: category,
|
|
||||||
)
|
|
||||||
expect(reviewable.reviewable_by_group_id).to eq(group.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "will not add add the group to the reviewable" do
|
|
||||||
SiteSetting.enable_category_group_moderation = false
|
|
||||||
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
||||||
expect(reviewable.reviewable_by_group_id).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it "will nullify the group_id if destroyed" do
|
|
||||||
reviewable = PostActionCreator.spam(user, post).reviewable
|
reviewable = PostActionCreator.spam(user, post).reviewable
|
||||||
group.destroy
|
group.destroy
|
||||||
expect(category.reload.reviewable_by_group).to be_blank
|
expect(category.reload.category_moderation_groups).to be_blank
|
||||||
expect(reviewable.reload.reviewable_by_group_id).to be_blank
|
|
||||||
end
|
|
||||||
|
|
||||||
it "will remove the reviewable_by_group if the category is updated" do
|
|
||||||
SiteSetting.enable_category_group_moderation = true
|
|
||||||
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
||||||
category.reviewable_by_group_id = nil
|
|
||||||
category.save!
|
|
||||||
expect(reviewable.reload.reviewable_by_group_id).to be_nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ RSpec.describe PostAction do
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
group.update!(messageable_level: Group::ALIAS_LEVELS[:nobody])
|
group.update!(messageable_level: Group::ALIAS_LEVELS[:nobody])
|
||||||
post.topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: post.topic.category, group:)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "notifies via pm" do
|
it "notifies via pm" do
|
||||||
|
|
|
@ -131,7 +131,9 @@ RSpec.describe Reviewable, type: :model do
|
||||||
it "works with the reviewable by group" do
|
it "works with the reviewable by group" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
reviewable.reviewable_by_group_id = group.id
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
reviewable.category_id = category.id
|
||||||
reviewable.save!
|
reviewable.save!
|
||||||
|
|
||||||
expect(Reviewable.list_for(user, status: :pending)).to be_empty
|
expect(Reviewable.list_for(user, status: :pending)).to be_empty
|
||||||
|
@ -147,7 +149,9 @@ RSpec.describe Reviewable, type: :model do
|
||||||
it "doesn't allow review by group when disabled" do
|
it "doesn't allow review by group when disabled" do
|
||||||
SiteSetting.enable_category_group_moderation = false
|
SiteSetting.enable_category_group_moderation = false
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
reviewable.reviewable_by_group_id = group.id
|
category = Fabricate(:category)
|
||||||
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
reviewable.category_id = category.id
|
||||||
reviewable.save!
|
reviewable.save!
|
||||||
|
|
||||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||||
|
@ -266,12 +270,12 @@ RSpec.describe Reviewable, type: :model do
|
||||||
fab!(:admin)
|
fab!(:admin)
|
||||||
fab!(:moderator)
|
fab!(:moderator)
|
||||||
fab!(:group)
|
fab!(:group)
|
||||||
|
fab!(:category)
|
||||||
fab!(:user) { Fabricate(:user, groups: [group]) }
|
fab!(:user) { Fabricate(:user, groups: [group]) }
|
||||||
fab!(:admin_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
fab!(:admin_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
||||||
fab!(:mod_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
fab!(:mod_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
||||||
fab!(:group_reviewable) do
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
||||||
Fabricate(:reviewable, reviewable_by_group: group, reviewable_by_moderator: false)
|
fab!(:group_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false, category:) }
|
||||||
end
|
|
||||||
|
|
||||||
context "for admins" do
|
context "for admins" do
|
||||||
it "returns a list of pending reviewables that haven't been seen by the user" do
|
it "returns a list of pending reviewables that haven't been seen by the user" do
|
||||||
|
@ -638,12 +642,12 @@ RSpec.describe Reviewable, type: :model do
|
||||||
|
|
||||||
describe ".unseen_reviewable_count" do
|
describe ".unseen_reviewable_count" do
|
||||||
fab!(:group)
|
fab!(:group)
|
||||||
|
fab!(:category)
|
||||||
fab!(:user)
|
fab!(:user)
|
||||||
fab!(:admin_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
fab!(:admin_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false) }
|
||||||
fab!(:mod_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
fab!(:mod_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: true) }
|
||||||
fab!(:group_reviewable) do
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
||||||
Fabricate(:reviewable, reviewable_by_moderator: false, reviewable_by_group: group)
|
fab!(:group_reviewable) { Fabricate(:reviewable, reviewable_by_moderator: false, category:) }
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't include reviewables that can't be seen by the user" do
|
it "doesn't include reviewables that can't be seen by the user" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
|
@ -3407,8 +3407,9 @@ RSpec.describe User do
|
||||||
user.update!(groups: [group])
|
user.update!(groups: [group])
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
||||||
group_reviewable =
|
category = Fabricate(:category)
|
||||||
Fabricate(:reviewable, reviewable_by_moderator: false, reviewable_by_group: group)
|
Fabricate(:category_moderation_group, category:, group:)
|
||||||
|
group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false, category:)
|
||||||
mod_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true)
|
mod_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true)
|
||||||
admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false)
|
admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false)
|
||||||
|
|
||||||
|
|
|
@ -517,7 +517,7 @@ RSpec.describe CategoriesController do
|
||||||
slug: "hello-cat",
|
slug: "hello-cat",
|
||||||
auto_close_hours: 72,
|
auto_close_hours: 72,
|
||||||
search_priority: Searchable::PRIORITIES[:ignore],
|
search_priority: Searchable::PRIORITIES[:ignore],
|
||||||
reviewable_by_group_name: group.name,
|
moderating_group_ids: [group.id],
|
||||||
permissions: {
|
permissions: {
|
||||||
"everyone" => readonly,
|
"everyone" => readonly,
|
||||||
"staff" => create_post,
|
"staff" => create_post,
|
||||||
|
@ -527,7 +527,7 @@ RSpec.describe CategoriesController do
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
cat_json = response.parsed_body["category"]
|
cat_json = response.parsed_body["category"]
|
||||||
expect(cat_json).to be_present
|
expect(cat_json).to be_present
|
||||||
expect(cat_json["reviewable_by_group_name"]).to eq(group.name)
|
expect(cat_json["moderating_group_ids"]).to eq([group.id])
|
||||||
expect(cat_json["name"]).to eq("hello")
|
expect(cat_json["name"]).to eq("hello")
|
||||||
expect(cat_json["slug"]).to eq("hello-cat")
|
expect(cat_json["slug"]).to eq("hello-cat")
|
||||||
expect(cat_json["color"]).to eq("ff0")
|
expect(cat_json["color"]).to eq("ff0")
|
||||||
|
@ -634,6 +634,10 @@ RSpec.describe CategoriesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#update" do
|
describe "#update" do
|
||||||
|
fab!(:mod_group_1) { Fabricate(:group) }
|
||||||
|
fab!(:mod_group_2) { Fabricate(:group) }
|
||||||
|
fab!(:mod_group_3) { Fabricate(:group) }
|
||||||
|
|
||||||
before { Jobs.run_immediately! }
|
before { Jobs.run_immediately! }
|
||||||
|
|
||||||
it "requires the user to be logged in" do
|
it "requires the user to be logged in" do
|
||||||
|
@ -871,6 +875,47 @@ RSpec.describe CategoriesController do
|
||||||
expect(category.custom_fields).to eq({ "field_1" => "hi" })
|
expect(category.custom_fields).to eq({ "field_1" => "hi" })
|
||||||
expect(category.form_template_ids.count).to eq(0)
|
expect(category.form_template_ids.count).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "doesn't set category moderation groups if the enable_category_group_moderation setting is false" do
|
||||||
|
SiteSetting.enable_category_group_moderation = false
|
||||||
|
|
||||||
|
put "/categories/#{category.id}.json", params: { moderating_group_ids: [mod_group_1.id] }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(category.reload.moderating_groups).to be_blank
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets category moderation groups if the enable_category_group_moderation setting is true" do
|
||||||
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
||||||
|
put "/categories/#{category.id}.json", params: { moderating_group_ids: [mod_group_1.id] }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(category.reload.moderating_groups).to contain_exactly(mod_group_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "removes category moderation groups and adds groups according to the moderating_group_ids param" do
|
||||||
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
||||||
|
category.update!(moderating_group_ids: [mod_group_2.id])
|
||||||
|
expect(category.reload.moderating_groups).to contain_exactly(mod_group_2)
|
||||||
|
|
||||||
|
put "/categories/#{category.id}.json",
|
||||||
|
params: {
|
||||||
|
moderating_group_ids: [mod_group_1.id, mod_group_3.id],
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(category.reload.moderating_groups).to contain_exactly(mod_group_1, mod_group_3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can remove all category moderation groups" do
|
||||||
|
SiteSetting.enable_category_group_moderation = true
|
||||||
|
|
||||||
|
category.update!(moderating_group_ids: [mod_group_2.id, mod_group_1.id])
|
||||||
|
expect(category.reload.moderating_groups).to contain_exactly(mod_group_2, mod_group_1)
|
||||||
|
|
||||||
|
put "/categories/#{category.id}.json", params: { moderating_group_ids: [] }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(category.reload.moderating_groups).to be_blank
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,7 +59,8 @@ RSpec.shared_examples "finding and showing post" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can find posts in the allowed category" do
|
it "can find posts in the allowed category" do
|
||||||
post.topic.category.update!(reviewable_by_group_id: group.id, topic_id: topic.id)
|
post.topic.category.update!(topic_id: topic.id)
|
||||||
|
Fabricate(:category_moderation_group, category: post.topic.category, group:)
|
||||||
get url
|
get url
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
@ -602,7 +603,8 @@ RSpec.describe PostsController do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
post.topic.category.update!(reviewable_by_group_id: group.id, topic_id: topic.id)
|
Fabricate(:category_moderation_group, category: post.topic.category, group:)
|
||||||
|
post.topic.category.update!(topic_id: topic.id)
|
||||||
sign_in(user_gm)
|
sign_in(user_gm)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2880,7 +2882,7 @@ RSpec.describe PostsController do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
topic.category.update!(reviewable_by_group_id: group.id)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
@ -2903,8 +2905,7 @@ RSpec.describe PostsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "prevents a group moderator from altering notes outside of their category" do
|
it "prevents a group moderator from altering notes outside of their category" do
|
||||||
moderatable_group = Fabricate(:group)
|
topic.category.category_moderation_groups.where(group:).delete_all
|
||||||
topic.category.update!(reviewable_by_group_id: moderatable_group.id)
|
|
||||||
|
|
||||||
put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello" }
|
put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello" }
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ RSpec.describe ReviewableClaimedTopicsController do
|
||||||
SiteSetting.reviewable_claiming = "optional"
|
SiteSetting.reviewable_claiming = "optional"
|
||||||
|
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
topic.category.update!(reviewable_by_group: group)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
|
|
||||||
messages =
|
messages =
|
||||||
MessageBus.track_publish("/reviewable_claimed") do
|
MessageBus.track_publish("/reviewable_claimed") do
|
||||||
|
@ -106,8 +106,7 @@ RSpec.describe ReviewableClaimedTopicsController do
|
||||||
not_notified = Fabricate(:user)
|
not_notified = Fabricate(:user)
|
||||||
|
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
topic.category.update!(reviewable_by_group: group)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
reviewable.update!(reviewable_by_group: group)
|
|
||||||
|
|
||||||
notified = Fabricate(:user)
|
notified = Fabricate(:user)
|
||||||
group.add(notified)
|
group.add(notified)
|
||||||
|
@ -190,8 +189,7 @@ RSpec.describe ReviewableClaimedTopicsController do
|
||||||
not_notified = Fabricate(:user)
|
not_notified = Fabricate(:user)
|
||||||
|
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
topic.category.update!(reviewable_by_group: group)
|
Fabricate(:category_moderation_group, category: topic.category, group:)
|
||||||
reviewable.update!(reviewable_by_group: group)
|
|
||||||
|
|
||||||
notified = Fabricate(:user)
|
notified = Fabricate(:user)
|
||||||
group.add(notified)
|
group.add(notified)
|
||||||
|
|
|
@ -233,7 +233,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "moving to a new topic as a group moderator" do
|
describe "moving to a new topic as a group moderator" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
|
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
|
||||||
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
|
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
|
||||||
|
@ -388,7 +391,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "moving to an existing topic as a group moderator" do
|
describe "moving to an existing topic as a group moderator" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
|
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
|
||||||
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
|
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
|
||||||
|
@ -439,7 +445,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "moving chronologically to an existing topic as a group moderator" do
|
describe "moving chronologically to an existing topic as a group moderator" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:p1) do
|
fab!(:p1) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
|
@ -715,7 +724,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "merging into another topic as a group moderator" do
|
describe "merging into another topic as a group moderator" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:p1) { Fabricate(:post, user: post_author1, post_number: 1, topic: topic) }
|
fab!(:p1) { Fabricate(:post, user: post_author1, post_number: 1, topic: topic) }
|
||||||
fab!(:p2) { Fabricate(:post, user: post_author2, post_number: 2, topic: topic) }
|
fab!(:p2) { Fabricate(:post, user: post_author2, post_number: 2, topic: topic) }
|
||||||
|
@ -752,7 +764,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "merging chronologically into another topic as a group moderator" do
|
describe "merging chronologically into another topic as a group moderator" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
fab!(:p1) do
|
fab!(:p1) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
|
@ -1152,7 +1167,10 @@ RSpec.describe TopicsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when logged in as a group member with reviewable status" do
|
describe "when logged in as a group member with reviewable status" do
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -4970,7 +4988,7 @@ RSpec.describe TopicsController do
|
||||||
it "allows a category moderator to create a delete timer" do
|
it "allows a category moderator to create a delete timer" do
|
||||||
user.update!(trust_level: TrustLevel[4])
|
user.update!(trust_level: TrustLevel[4])
|
||||||
Group.user_trust_level_change!(user.id, user.trust_level)
|
Group.user_trust_level_change!(user.id, user.trust_level)
|
||||||
topic.category.update!(reviewable_by_group: user.groups.first)
|
Fabricate(:category_moderation_group, category: topic.category, group: user.groups.first)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,19 @@ RSpec.describe CategorySerializer do
|
||||||
fab!(:user)
|
fab!(:user)
|
||||||
fab!(:admin)
|
fab!(:admin)
|
||||||
fab!(:group)
|
fab!(:group)
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group_id: group.id) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
||||||
|
|
||||||
it "includes the reviewable by group name if enabled" do
|
it "includes the reviewable by group name if enabled" do
|
||||||
SiteSetting.enable_category_group_moderation = true
|
SiteSetting.enable_category_group_moderation = true
|
||||||
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
||||||
expect(json[:reviewable_by_group_name]).to eq(group.name)
|
expect(json[:moderating_group_ids]).to eq([group.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't include the reviewable by group name if disabled" do
|
it "doesn't include the reviewable by group name if disabled" do
|
||||||
SiteSetting.enable_category_group_moderation = false
|
SiteSetting.enable_category_group_moderation = false
|
||||||
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
||||||
expect(json[:reviewable_by_group_name]).to be_blank
|
expect(json[:moderating_group_ids]).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
it "includes custom fields" do
|
it "includes custom fields" do
|
||||||
|
|
|
@ -284,12 +284,12 @@ RSpec.describe PostSerializer do
|
||||||
fab!(:topic)
|
fab!(:topic)
|
||||||
fab!(:group_user)
|
fab!(:group_user)
|
||||||
fab!(:post) { Fabricate(:post, topic: topic) }
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
before do
|
Fabricate(:category_moderation_group, category: topic.category, group: group_user.group)
|
||||||
SiteSetting.enable_category_group_moderation = true
|
|
||||||
topic.category.update!(reviewable_by_group_id: group_user.group.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before { SiteSetting.enable_category_group_moderation = true }
|
||||||
|
|
||||||
it "does nothing for regular users" do
|
it "does nothing for regular users" do
|
||||||
expect(serialized_post_for_user(nil)[:group_moderator]).to eq(nil)
|
expect(serialized_post_for_user(nil)[:group_moderator]).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -454,7 +454,10 @@ RSpec.describe TopicViewSerializer do
|
||||||
|
|
||||||
context "with can_edit" do
|
context "with can_edit" do
|
||||||
fab!(:group_user)
|
fab!(:group_user)
|
||||||
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
|
fab!(:category)
|
||||||
|
fab!(:category_moderation_group) do
|
||||||
|
Fabricate(:category_moderation_group, category:, group: group_user.group)
|
||||||
|
end
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
let(:user) { group_user.user }
|
let(:user) { group_user.user }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue