mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-02 12:26:37 +08:00
Category approval was previously a simple boolean toggle per post type (`require_topic_approval` / `require_reply_approval`). This made it impossible to exempt specific groups from review or to require review only for certain groups. This replaces the boolean model with a four-mode enum on `CategorySetting`: `no_one`, `everyone`, `everyone_except`, and `no_one_except`. The group-based modes pair with `CategoryPostingReviewGroup` join records to determine which groups are included or excluded. The old boolean accessors are preserved as aliases of the enum predicates for backward compatibility. With the mode now living on `CategorySetting`, the per-row `permission` column on `CategoryPostingReviewGroup` is redundant since the join table only needs to track which groups are associated with a category, not what kind of permission they have. The column is made nullable and marked readonly in a pre-deploy migration, then dropped in a post-deploy migration. On the frontend, the approval checkboxes are replaced with `ComboBox` dropdowns for the four modes and a conditional `GroupChooser` for the group-based modes, in both the legacy and simplified category editors. The simplified editor uses FormKit field-level validation to show inline errors when a group-based mode is selected without any groups. The legacy editor relies on server-side validation surfaced through `popupAjaxError`.
279 lines
9.3 KiB
Ruby
279 lines
9.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe CategorySerializer do
|
|
fab!(:user)
|
|
fab!(:admin)
|
|
fab!(:group)
|
|
fab!(:category)
|
|
fab!(:category_moderation_group) { Fabricate(:category_moderation_group, category:, group:) }
|
|
|
|
it "includes the reviewable by group name if enabled" do
|
|
SiteSetting.enable_category_group_moderation = true
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
expect(json[:moderating_group_ids]).to eq([group.id])
|
|
end
|
|
|
|
it "doesn't include the reviewable by group name if disabled" do
|
|
SiteSetting.enable_category_group_moderation = false
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
expect(json[:moderating_group_ids]).to be_blank
|
|
end
|
|
|
|
it "includes custom fields" do
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
expect(json[:custom_fields]).to be_empty
|
|
|
|
category.custom_fields["enable_marketplace"] = true
|
|
category.save_custom_fields
|
|
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
expect(json[:custom_fields]).to be_present
|
|
end
|
|
|
|
it "does not include the default notification level when there is no user" do
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
expect(json.key?(:notification_level)).to eq(false)
|
|
end
|
|
|
|
describe "user notification level" do
|
|
it "includes the user's notification level" do
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
NotificationLevels.all[:watching],
|
|
category.id,
|
|
)
|
|
json = described_class.new(category, scope: Guardian.new(user), root: false).as_json
|
|
expect(json[:notification_level]).to eq(NotificationLevels.all[:watching])
|
|
end
|
|
end
|
|
|
|
describe "#group_permissions" do
|
|
fab!(:private_group) do
|
|
Fabricate(:group, visibility_level: Group.visibility_levels[:staff], name: "bbb")
|
|
end
|
|
|
|
fab!(:user_group) do
|
|
Fabricate(:group, visibility_level: Group.visibility_levels[:members], name: "ccc").tap do |g|
|
|
g.add(user)
|
|
end
|
|
end
|
|
|
|
before do
|
|
group.update!(name: "aaa")
|
|
|
|
category.set_permissions(
|
|
:everyone => :readonly,
|
|
group.name => :readonly,
|
|
user_group.name => :full,
|
|
private_group.name => :full,
|
|
)
|
|
|
|
category.save!
|
|
end
|
|
|
|
it "does not include the attribute for an anon user" do
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
|
|
expect(json[:group_permissions]).to eq(nil)
|
|
end
|
|
|
|
it "does not include the attribute for a regular user" do
|
|
json = described_class.new(category, scope: Guardian.new(user), root: false).as_json
|
|
|
|
expect(json[:group_permissions]).to eq(nil)
|
|
end
|
|
|
|
it "returns the right category group permissions for a user that can edit the category" do
|
|
SiteSetting.moderators_manage_categories = true
|
|
user.update!(moderator: true)
|
|
|
|
json = described_class.new(category, scope: Guardian.new(user), root: false).as_json
|
|
|
|
expect(json[:group_permissions]).to eq(
|
|
[
|
|
{
|
|
permission_type: CategoryGroup.permission_types[:readonly],
|
|
group_name: group.name,
|
|
group_id: group.id,
|
|
},
|
|
{
|
|
permission_type: CategoryGroup.permission_types[:full],
|
|
group_name: private_group.name,
|
|
group_id: private_group.id,
|
|
},
|
|
{
|
|
permission_type: CategoryGroup.permission_types[:full],
|
|
group_name: user_group.name,
|
|
group_id: user_group.id,
|
|
},
|
|
{
|
|
permission_type: CategoryGroup.permission_types[:readonly],
|
|
group_name: "everyone",
|
|
group_id: Group::AUTO_GROUPS[:everyone],
|
|
},
|
|
],
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "available groups" do
|
|
it "not included for a regular user" do
|
|
json = described_class.new(category, scope: Guardian.new(user), root: false).as_json
|
|
expect(json[:available_groups]).to eq(nil)
|
|
end
|
|
|
|
it "included for an admin" do
|
|
json = described_class.new(category, scope: Guardian.new(admin), root: false).as_json
|
|
expect(json[:available_groups]).to eq(Group.order(:name).pluck(:name) - ["everyone"])
|
|
end
|
|
end
|
|
|
|
describe "name and description" do
|
|
fab!(:category_with_localization) do
|
|
Fabricate(:category, name: "Original Name", description: "Original Description", locale: "en")
|
|
end
|
|
|
|
before do
|
|
CategoryLocalization.create!(
|
|
category: category_with_localization,
|
|
locale: "ja",
|
|
name: "日本語名",
|
|
description: "<p>最初の段落</p><p>二番目の段落</p>",
|
|
)
|
|
end
|
|
|
|
it "returns untranslated name and description for CategorySerializer" do
|
|
json =
|
|
described_class.new(
|
|
category_with_localization,
|
|
scope: Guardian.new(user),
|
|
root: false,
|
|
).as_json
|
|
expect(json[:name]).to eq("Original Name")
|
|
expect(json[:description]).to eq("Original Description")
|
|
end
|
|
|
|
it "returns translated attributes for SiteCategorySerializer when enabled" do
|
|
SiteSetting.content_localization_enabled = true
|
|
user.update!(locale: "ja")
|
|
I18n.with_locale("ja") do
|
|
json =
|
|
SiteCategorySerializer.new(
|
|
category_with_localization,
|
|
scope: Guardian.new(user),
|
|
root: false,
|
|
).as_json
|
|
expect(json[:name]).to eq("日本語名")
|
|
expect(json[:description]).to eq("<p>最初の段落</p><p>二番目の段落</p>")
|
|
expect(json[:description_text]).to eq("<p>最初の段落</p><p>二番目の段落</p>")
|
|
expect(json[:description_excerpt]).to eq("最初の段落 二番目の段落")
|
|
end
|
|
end
|
|
|
|
it "returns untranslated attributes for SiteCategorySerializer when disabled" do
|
|
SiteSetting.content_localization_enabled = false
|
|
user.update!(locale: "ja")
|
|
I18n.with_locale("ja") do
|
|
json =
|
|
SiteCategorySerializer.new(
|
|
category_with_localization,
|
|
scope: Guardian.new(user),
|
|
root: false,
|
|
).as_json
|
|
expect(json[:name]).to eq("Original Name")
|
|
expect(json[:description]).to eq("Original Description")
|
|
expect(json[:description_text]).to eq(category_with_localization.description_text)
|
|
expect(json[:description_excerpt]).to eq(category_with_localization.description_excerpt)
|
|
end
|
|
end
|
|
|
|
it "returns untranslated name and description for BasicCategorySerializer" do
|
|
json =
|
|
BasicCategorySerializer.new(
|
|
category_with_localization,
|
|
scope: Guardian.new(user),
|
|
root: false,
|
|
).as_json
|
|
expect(json[:name]).to eq("Original Name")
|
|
expect(json[:description]).to eq("Original Description")
|
|
end
|
|
end
|
|
|
|
describe "#topic_posting_review_group_ids" do
|
|
it "returns group ids when groups exist" do
|
|
category.update!(
|
|
topic_posting_review_mode: :everyone_except,
|
|
topic_posting_review_group_ids: [group.id],
|
|
)
|
|
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
|
|
expect(json[:topic_posting_review_group_ids]).to eq([group.id])
|
|
end
|
|
|
|
it "returns empty array when no groups are associated" do
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
|
|
expect(json[:topic_posting_review_group_ids]).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "#reply_posting_review_group_ids" do
|
|
it "returns group ids when groups exist" do
|
|
category.update!(
|
|
reply_posting_review_mode: :no_one_except,
|
|
reply_posting_review_group_ids: [group.id],
|
|
)
|
|
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
|
|
expect(json[:reply_posting_review_group_ids]).to eq([group.id])
|
|
end
|
|
|
|
it "returns empty array when no groups are associated" do
|
|
json = described_class.new(category, scope: Guardian.new, root: false).as_json
|
|
|
|
expect(json[:reply_posting_review_group_ids]).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "#require_topic_approval" do
|
|
it "returns false when topic approval is not required" do
|
|
category.require_topic_approval = false
|
|
category.save!
|
|
|
|
json = described_class.new(category, scope: Guardian.new(admin), root: false).as_json
|
|
|
|
expect(json[:category_setting][:require_topic_approval]).to eq(false)
|
|
end
|
|
|
|
it "returns true when topic approval is required" do
|
|
category.require_topic_approval = true
|
|
category.save!
|
|
|
|
json = described_class.new(category, scope: Guardian.new(admin), root: false).as_json
|
|
|
|
expect(json[:category_setting][:require_topic_approval]).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe "#require_reply_approval" do
|
|
it "returns false when reply approval is not required" do
|
|
category.require_reply_approval = false
|
|
category.save!
|
|
|
|
json = described_class.new(category, scope: Guardian.new(admin), root: false).as_json
|
|
|
|
expect(json[:category_setting][:require_reply_approval]).to eq(false)
|
|
end
|
|
|
|
it "returns true when reply approval is required" do
|
|
category.require_reply_approval = true
|
|
category.save!
|
|
|
|
json = described_class.new(category, scope: Guardian.new(admin), root: false).as_json
|
|
|
|
expect(json[:category_setting][:require_reply_approval]).to eq(true)
|
|
end
|
|
end
|
|
end
|