FEATURE: Default Composer Category Site Setting (#18967)
* FEATURE: Default Composer Category Site Setting - Create the default_composer_category site setting - Replace general_category_id logic for auto selecting the composer category - Prevent Uncategorized from being selected if not allowed - Add default_composer_category option to seeded categories - Create a migration to populate the default_composer_category site setting if there is a general_category_id populated - Added some tests * Add missing translation for the new site setting * fix some js tests * Just check that the header value is null
This commit is contained in:
parent
8c48285145
commit
7be53b1588
|
@ -143,10 +143,15 @@ const Composer = RestModel.extend({
|
|||
const oldCategoryId = this._categoryId;
|
||||
|
||||
if (isEmpty(categoryId)) {
|
||||
// Set General as the default category
|
||||
const generalCategoryId = this.siteSettings.general_category_id;
|
||||
// Check if there is a default composer category to set
|
||||
const defaultComposerCategoryId = parseInt(
|
||||
this.siteSettings.default_composer_category,
|
||||
10
|
||||
);
|
||||
categoryId =
|
||||
generalCategoryId && generalCategoryId > 0 ? generalCategoryId : null;
|
||||
defaultComposerCategoryId && defaultComposerCategoryId > 0
|
||||
? defaultComposerCategoryId
|
||||
: null;
|
||||
}
|
||||
this._categoryId = categoryId;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ acceptance("Composer", function (needs) {
|
|||
needs.settings({
|
||||
enable_whispers: true,
|
||||
general_category_id: 1,
|
||||
default_composer_category: 1,
|
||||
});
|
||||
needs.site({
|
||||
can_tag_topics: true,
|
||||
|
@ -90,7 +91,7 @@ acceptance("Composer", function (needs) {
|
|||
test("Composer is opened", async function (assert) {
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
// Check that General category is selected
|
||||
// Check that the default category is selected
|
||||
assert.strictEqual(selectKit(".category-chooser").header().value(), "1");
|
||||
|
||||
assert.strictEqual(
|
||||
|
@ -1201,3 +1202,109 @@ acceptance("Composer - Focus Open and Closed", function (needs) {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Default Composer Category tests
|
||||
acceptance("Composer - Default category", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
general_category_id: 1,
|
||||
default_composer_category: 2,
|
||||
});
|
||||
needs.site({
|
||||
categories: [
|
||||
{
|
||||
id: 1,
|
||||
name: "General",
|
||||
slug: "general",
|
||||
permission: 1,
|
||||
ltopic_template: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "test too",
|
||||
slug: "test-too",
|
||||
permission: 1,
|
||||
topic_template: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
test("Default category is selected over general category", async function (assert) {
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
assert.strictEqual(selectKit(".category-chooser").header().value(), "2");
|
||||
assert.strictEqual(
|
||||
selectKit(".category-chooser").header().name(),
|
||||
"test too"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Composer - Uncategorized category", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
general_category_id: -1, // For sites that never had this seeded
|
||||
default_composer_category: -1, // For sites that never had this seeded
|
||||
allow_uncategorized_topics: true,
|
||||
});
|
||||
needs.site({
|
||||
categories: [
|
||||
{
|
||||
id: 1,
|
||||
name: "General",
|
||||
slug: "general",
|
||||
permission: 1,
|
||||
ltopic_template: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "test too",
|
||||
slug: "test-too",
|
||||
permission: 1,
|
||||
topic_template: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
test("Uncategorized category is selected", async function (assert) {
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
assert.strictEqual(selectKit(".category-chooser").header().value(), null);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Composer - default category not set", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
default_composer_category: "",
|
||||
});
|
||||
needs.site({
|
||||
categories: [
|
||||
{
|
||||
id: 1,
|
||||
name: "General",
|
||||
slug: "general",
|
||||
permission: 1,
|
||||
ltopic_template: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "test too",
|
||||
slug: "test-too",
|
||||
permission: 1,
|
||||
topic_template: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
test("Nothing is selected", async function (assert) {
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
assert.strictEqual(selectKit(".category-chooser").header().value(), null);
|
||||
assert.strictEqual(
|
||||
selectKit(".category-chooser").header().name(),
|
||||
"category…"
|
||||
);
|
||||
});
|
||||
});
|
||||
// END: Default Composer Category tests
|
||||
|
|
|
@ -126,9 +126,9 @@ module(
|
|||
assert.strictEqual(this.subject.header().label(), "category…");
|
||||
});
|
||||
|
||||
test("with allowUncategorized=null and generalCategoryId present", async function (assert) {
|
||||
test("with allowUncategorized=null and defaultComposerCategory present", async function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
this.siteSettings.general_category_id = 4;
|
||||
this.siteSettings.default_composer_category = 4;
|
||||
|
||||
await render(hbs`
|
||||
<CategoryChooser
|
||||
|
@ -143,9 +143,9 @@ module(
|
|||
assert.strictEqual(this.subject.header().label(), "");
|
||||
});
|
||||
|
||||
test("with allowUncategorized=null and generalCategoryId present, but not set", async function (assert) {
|
||||
test("with allowUncategorized=null and defaultComposerCategory present, but not set", async function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
this.siteSettings.general_category_id = -1;
|
||||
this.siteSettings.default_composer_category = -1;
|
||||
|
||||
await render(hbs`
|
||||
<CategoryChooser
|
||||
|
|
|
@ -44,8 +44,11 @@ export default ComboBoxComponent.extend({
|
|||
) {
|
||||
return Category.findUncategorized();
|
||||
} else {
|
||||
const generalCategoryId = this.siteSettings.general_category_id;
|
||||
if (!generalCategoryId || generalCategoryId < 0) {
|
||||
const defaultCategoryId = parseInt(
|
||||
this.siteSettings.default_composer_category,
|
||||
10
|
||||
);
|
||||
if (!defaultCategoryId || defaultCategoryId < 0) {
|
||||
return this.defaultItem(null, htmlSafe(I18n.t("category.choose")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1495,6 +1495,7 @@ en:
|
|||
search_ignore_accents: "Ignore accents when searching for text."
|
||||
category_search_priority_low_weight: "Weight applied to ranking for low category search priority."
|
||||
category_search_priority_high_weight: "Weight applied to ranking for high category search priority."
|
||||
default_composer_category: "The category used to pre-populate the category dropdown when creating a new topic."
|
||||
allow_uncategorized_topics: "Allow topics to be created without a category. WARNING: If there are any uncategorized topics, you must recategorize them before turning this off."
|
||||
allow_duplicate_topic_titles: "Allow topics with identical, duplicate titles."
|
||||
allow_duplicate_topic_titles_category: "Allow topics with identical, duplicate titles if the category is different. allow_duplicate_topic_titles must be disabled."
|
||||
|
@ -2437,6 +2438,7 @@ en:
|
|||
search_tokenize_japanese_enabled: "You must disable 'search_tokenize_japanese' before enabling this setting."
|
||||
discourse_connect_cannot_be_enabled_if_second_factor_enforced: "You cannot enable DiscourseConnect if 2FA is enforced."
|
||||
delete_rejected_email_after_days: "This setting cannot be set lower than the delete_email_logs_after_days setting or greater than %{max}"
|
||||
invalid_uncategorized_category_setting: "The Uncategorized category cannot be selected if allow uncategorized topics is not allowed"
|
||||
|
||||
placeholder:
|
||||
discourse_connect_provider_secrets:
|
||||
|
|
|
@ -2339,6 +2339,11 @@ uncategorized:
|
|||
uncategorized_category_id:
|
||||
default: -1
|
||||
hidden: true
|
||||
default_composer_category:
|
||||
client: true
|
||||
type: category
|
||||
default: ""
|
||||
validator: "DefaultComposerCategoryValidator"
|
||||
|
||||
notify_about_flags_after:
|
||||
type: float
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
## If the general_category_id has a value, set the *new*
|
||||
# default_composer_category site setting to match
|
||||
|
||||
class PopulateDefaultComposerCategory < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
general_category_id = DB.query_single("SELECT value FROM site_settings WHERE name = 'general_category_id'")
|
||||
return if general_category_id.blank? || general_category_id[0].to_i < 0
|
||||
default_composer_category = DB.query_single("SELECT value FROM site_settings where name = 'default_composer_category'")
|
||||
return if !default_composer_category.blank?
|
||||
DB.exec(
|
||||
"INSERT INTO site_settings(name, value, data_type, created_at, updated_at)
|
||||
VALUES('default_composer_category', :setting, '16', NOW(), NOW())",
|
||||
setting: general_category_id[0].to_i
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
|
@ -87,7 +87,8 @@ module SeedData
|
|||
text_color: 'FFFFFF',
|
||||
permissions: { everyone: :full },
|
||||
force_permissions: true,
|
||||
sidebar: true
|
||||
sidebar: true,
|
||||
default_composer_category: true
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -99,7 +100,7 @@ module SeedData
|
|||
end
|
||||
|
||||
def create_category(site_setting_name:, name:, description:, position:, color:, text_color:,
|
||||
permissions:, force_permissions:, force_existence: false, sidebar: false)
|
||||
permissions:, force_permissions:, force_existence: false, sidebar: false, default_composer_category: false)
|
||||
category_id = SiteSetting.get(site_setting_name)
|
||||
|
||||
if should_create_category?(category_id, force_existence)
|
||||
|
@ -123,6 +124,10 @@ module SeedData
|
|||
sidebar_categories << category.id
|
||||
SiteSetting.set('default_sidebar_categories', sidebar_categories.join('|'))
|
||||
end
|
||||
|
||||
if default_composer_category
|
||||
SiteSetting.set('default_composer_category', category.id)
|
||||
end
|
||||
elsif category = Category.find_by(id: category_id)
|
||||
if description.present? && (category.topic_id.blank? || !Topic.exists?(category.topic_id))
|
||||
category.description = description
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DefaultComposerCategoryValidator
|
||||
def initialize(opts = {})
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def valid_value?(val)
|
||||
category_id = val.to_i
|
||||
unless SiteSetting.allow_uncategorized_topics
|
||||
return false if category_id == SiteSetting.uncategorized_category_id
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def error_message
|
||||
I18n.t('site_settings.errors.invalid_uncategorized_category_setting')
|
||||
end
|
||||
end
|
|
@ -107,6 +107,7 @@ RSpec.describe SeedData::Categories do
|
|||
.and change { Topic.count }
|
||||
|
||||
expect(Category.last.name).to eq("General")
|
||||
expect(SiteSetting.default_composer_category).to eq(Category.last.id)
|
||||
end
|
||||
|
||||
it "adds default categories SiteSetting.default_sidebar_categories" do
|
||||
|
|
Loading…
Reference in New Issue