mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-04-30 12:48:01 +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`.
128 lines
5 KiB
Ruby
128 lines
5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
def write_template(path, task_name, template)
|
|
header = <<~JS
|
|
// DO NOT EDIT THIS FILE!!!
|
|
// Update it by running `rake javascript:#{task_name}`
|
|
JS
|
|
|
|
basename = File.basename(path)
|
|
output_path = "#{Rails.root}/frontend/#{path}"
|
|
|
|
File.write(output_path, "#{header}\n\n#{template}")
|
|
puts "#{basename} created"
|
|
system("pnpm prettier --write #{output_path}", exception: true)
|
|
puts "#{basename} prettified"
|
|
end
|
|
|
|
task "javascript:update_constants" => :environment do
|
|
task_name = "update_constants"
|
|
|
|
auto_groups =
|
|
Group::AUTO_GROUPS.inject({}) do |result, (group_name, group_id)|
|
|
result.merge(group_name => { id: group_id, automatic: true, name: group_name })
|
|
end
|
|
|
|
MAIN_FONT_KEYS = %w[helvetica inter lato montserrat open_sans poppins roboto merriweather mukta]
|
|
|
|
write_template("discourse/admin/lib/constants.js", task_name, <<~JS)
|
|
export const ADMIN_SEARCH_RESULT_TYPES = #{Admin::SearchController::RESULT_TYPES.to_json};
|
|
|
|
export const SITE_SETTING_REQUIRES_CONFIRMATION_TYPES = #{SiteSettings::TypeSupervisor::REQUIRES_CONFIRMATION_TYPES.to_json};
|
|
|
|
export const API_KEY_SCOPE_MODES = #{ApiKey.scope_modes.keys.to_json}
|
|
|
|
export const SYSTEM_FLAG_IDS = #{PostActionType.types.to_json};
|
|
|
|
export const REPORT_MODES = #{Report::MODES.to_json};
|
|
|
|
export const USER_FIELD_FLAGS = #{UserField::FLAG_ATTRIBUTES};
|
|
|
|
export const DEFAULT_USER_PREFERENCES = #{SiteSetting::DEFAULT_USER_PREFERENCES.to_json};
|
|
|
|
export const MAIN_FONTS = #{DiscourseFonts.fonts.filter { |font| MAIN_FONT_KEYS.include?(font[:key]) }.map { |font| { key: font[:key], name: font[:name] } }.to_json}
|
|
|
|
export const MORE_FONTS = #{DiscourseFonts.fonts.reject { |font| MAIN_FONT_KEYS.include?(font[:key]) }.map { |font| { key: font[:key], name: font[:name] } }.to_json}
|
|
|
|
export const DEFAULT_TEXT_SIZES = #{DefaultTextSizeSetting::DEFAULT_TEXT_SIZES}
|
|
JS
|
|
|
|
write_template("discourse/app/lib/constants.js", task_name, <<~JS)
|
|
export const SEARCH_PRIORITIES = #{Searchable::PRIORITIES.to_json};
|
|
|
|
export const SEARCH_PHRASE_REGEXP = '#{Search::PHRASE_MATCH_REGEXP_PATTERN}';
|
|
|
|
export const SIDEBAR_URL = {
|
|
max_icon_length: #{SidebarUrl::MAX_ICON_LENGTH},
|
|
max_name_length: #{SidebarUrl::MAX_NAME_LENGTH},
|
|
max_value_length: #{SidebarUrl::MAX_VALUE_LENGTH}
|
|
}
|
|
|
|
export const SIDEBAR_SECTION = {
|
|
max_title_length: #{SidebarSection::MAX_TITLE_LENGTH},
|
|
}
|
|
|
|
export const CATEGORY_STYLE_TYPES = #{Category.style_types.to_json};
|
|
|
|
export const CATEGORY_TEXT_COLORS = #{Category::DEFAULT_TEXT_COLORS};
|
|
|
|
// NOTE: Group names are changed based on the site's locale, see
|
|
// Group.refresh_automatic_group! for more details
|
|
export const AUTO_GROUPS = #{auto_groups.to_json};
|
|
|
|
export const GROUP_SMTP_SSL_MODES = #{Group.smtp_ssl_modes.to_json};
|
|
|
|
export const GROUP_VISIBILITY_LEVELS = #{Group.visibility_levels.to_json};
|
|
|
|
export const MAX_AUTO_MEMBERSHIP_DOMAINS_LOOKUP = #{Admin::GroupsController::MAX_AUTO_MEMBERSHIP_DOMAINS_LOOKUP};
|
|
|
|
export const MAX_NOTIFICATIONS_LIMIT_PARAMS = #{NotificationsController::INDEX_LIMIT};
|
|
|
|
export const TOPIC_VISIBILITY_REASONS = #{Topic.visibility_reasons.to_json};
|
|
|
|
export const MAX_UNOPTIMIZED_CATEGORIES = #{CategoryList::MAX_UNOPTIMIZED_CATEGORIES};
|
|
|
|
export const REVIEWABLE_UNKNOWN_TYPE_SOURCE = "#{Reviewable::UNKNOWN_TYPE_SOURCE}";
|
|
|
|
export const ADMIN_SEARCH_RESULT_TYPES = #{Admin::SearchController::RESULT_TYPES.to_json};
|
|
|
|
export const API_KEY_SCOPE_MODES = #{ApiKey.scope_modes.keys.to_json};
|
|
|
|
export const INVITE_DESCRIPTION_MAX_LENGTH = #{Invite::DESCRIPTION_MAX_LENGTH};
|
|
|
|
export const POSTING_REVIEW_GROUP_BASED_MODES = #{CategorySetting::GROUP_BASED_MODES.to_json};
|
|
|
|
export const USER_OPTION_COMPOSITION_MODES = #{UserOption.composition_mode_types.to_json};
|
|
|
|
export const UPCOMING_CHANGES_USER_ENABLED_REASONS = #{UpcomingChanges.user_enabled_reasons.to_json};
|
|
|
|
export const INTERFACE_COLOR_MODES = {
|
|
AUTO: #{UserOption::AUTO_MODE},
|
|
LIGHT: #{UserOption::LIGHT_MODE},
|
|
DARK: #{UserOption::DARK_MODE},
|
|
};
|
|
JS
|
|
|
|
pretty_notifications = Notification.types.map { |n| " #{n[0]}: #{n[1]}," }.join("\n")
|
|
|
|
write_template("discourse/tests/fixtures/concerns/notification-types.js", task_name, <<~JS)
|
|
export const NOTIFICATION_TYPES = {
|
|
#{pretty_notifications}
|
|
};
|
|
JS
|
|
|
|
require "emoji/regex_generator"
|
|
|
|
write_template("pretty-text/addon/emoji/data.js", task_name, <<~JS)
|
|
export const emojis = new Set(#{Emoji.standard.map(&:name).flatten.inspect});
|
|
export const tonableEmojis = #{Emoji.tonable_emojis.flatten.inspect};
|
|
export const aliases = #{Emoji.aliases.inspect.gsub("=>", ":")};
|
|
export const translations = #{Emoji.translations.inspect.gsub("=>", ":")};
|
|
export const replacements = #{Emoji.unicode_replacements_json};
|
|
export const emojiReplacementRegex = "#{Emoji::RegexGenerator.generate}";
|
|
JS
|
|
|
|
write_template("pretty-text/addon/emoji/version.js", task_name, <<~JS)
|
|
export const IMAGE_VERSION = "#{Emoji::EMOJI_VERSION}";
|
|
JS
|
|
end
|