mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FIX: ensures minimum tags logic is correct and shared (#14723)
Also fixes a bug where select-kit was not updating noneItem in multi-selects.
This commit is contained in:
parent
362c47ce6a
commit
904d509cce
@ -1,3 +1,4 @@
|
|||||||
|
import Category from "discourse/models/category";
|
||||||
import Controller, { inject as controller } from "@ember/controller";
|
import Controller, { inject as controller } from "@ember/controller";
|
||||||
import DiscourseURL, { userPath } from "discourse/lib/url";
|
import DiscourseURL, { userPath } from "discourse/lib/url";
|
||||||
import { alias, and, not, or } from "@ember/object/computed";
|
import { alias, and, not, or } from "@ember/object/computed";
|
||||||
@ -212,11 +213,9 @@ export default Controller.extend(bufferedProperty("model"), {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("model.category")
|
@discourseComputed("buffered.category_id")
|
||||||
minimumRequiredTags(category) {
|
minimumRequiredTags(categoryId) {
|
||||||
return category && category.minimum_required_tags > 0
|
return Category.findById(categoryId)?.minimumRequiredTags || 0;
|
||||||
? category.minimum_required_tags
|
|
||||||
: null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_removeDeleteOnOwnerReplyBookmarks() {
|
_removeDeleteOnOwnerReplyBookmarks() {
|
||||||
|
@ -42,6 +42,19 @@ const Category = RestModel.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"required_tag_groups",
|
||||||
|
"min_tags_from_required_group",
|
||||||
|
"minimum_required_tags"
|
||||||
|
)
|
||||||
|
minimumRequiredTags() {
|
||||||
|
if (this.required_tag_groups) {
|
||||||
|
return this.min_tags_from_required_group;
|
||||||
|
} else {
|
||||||
|
return this.minimum_required_tags > 0 ? this.minimum_required_tags : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
availablePermissions() {
|
availablePermissions() {
|
||||||
return [
|
return [
|
||||||
|
@ -157,26 +157,9 @@ const Composer = RestModel.extend({
|
|||||||
return categoryId ? this.site.categories.findBy("id", categoryId) : null;
|
return categoryId ? this.site.categories.findBy("id", categoryId) : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("category")
|
@discourseComputed("category.minimumRequiredTags")
|
||||||
minimumRequiredTags(category) {
|
minimumRequiredTags(minimumRequiredTags) {
|
||||||
if (category) {
|
return minimumRequiredTags || 0;
|
||||||
if (category.required_tag_groups) {
|
|
||||||
return category.min_tags_from_required_group;
|
|
||||||
} else {
|
|
||||||
return category.minimum_required_tags > 0
|
|
||||||
? category.minimum_required_tags
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("category")
|
|
||||||
requiredTagGroups(category) {
|
|
||||||
return category && category.required_tag_groups
|
|
||||||
? category.required_tag_groups
|
|
||||||
: null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
creatingTopic: equal("action", CREATE_TOPIC),
|
creatingTopic: equal("action", CREATE_TOPIC),
|
||||||
|
@ -99,7 +99,6 @@
|
|||||||
options=(hash
|
options=(hash
|
||||||
categoryId=model.categoryId
|
categoryId=model.categoryId
|
||||||
minimum=model.minimumRequiredTags
|
minimum=model.minimumRequiredTags
|
||||||
requiredTagGroups=model.requiredTagGroups
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
{{popup-input-tip validation=tagValidation}}
|
{{popup-input-tip validation=tagValidation}}
|
||||||
|
@ -221,6 +221,50 @@ module("Unit | Model | category", function () {
|
|||||||
assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz);
|
assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("minimumRequiredTags", function (assert) {
|
||||||
|
const store = createStore();
|
||||||
|
|
||||||
|
let foo = store.createRecord("category", {
|
||||||
|
id: 1,
|
||||||
|
slug: "foo",
|
||||||
|
required_tag_groups: ["bar"],
|
||||||
|
min_tags_from_required_group: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(foo.minimumRequiredTags, 2);
|
||||||
|
|
||||||
|
foo = store.createRecord("category", {
|
||||||
|
id: 2,
|
||||||
|
slug: "foo",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(foo.minimumRequiredTags, null);
|
||||||
|
|
||||||
|
foo = store.createRecord("category", {
|
||||||
|
id: 3,
|
||||||
|
slug: "foo",
|
||||||
|
minimum_required_tags: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(foo.minimumRequiredTags, null);
|
||||||
|
|
||||||
|
foo = store.createRecord("category", {
|
||||||
|
id: 4,
|
||||||
|
slug: "foo",
|
||||||
|
minimum_required_tags: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(foo.minimumRequiredTags, 2);
|
||||||
|
|
||||||
|
foo = store.createRecord("category", {
|
||||||
|
id: 5,
|
||||||
|
slug: "foo",
|
||||||
|
min_tags_from_required_group: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(foo.minimumRequiredTags, null);
|
||||||
|
});
|
||||||
|
|
||||||
test("search with category name", function (assert) {
|
test("search with category name", function (assert) {
|
||||||
const store = createStore(),
|
const store = createStore(),
|
||||||
category1 = store.createRecord("category", {
|
category1 = store.createRecord("category", {
|
||||||
|
@ -20,6 +20,7 @@ export default ComboBoxComponent.extend({
|
|||||||
permissionType: PermissionType.FULL,
|
permissionType: PermissionType.FULL,
|
||||||
excludeCategoryId: null,
|
excludeCategoryId: null,
|
||||||
scopedCategoryId: null,
|
scopedCategoryId: null,
|
||||||
|
prioritizedCategoryId: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
modifyComponentForRow() {
|
modifyComponentForRow() {
|
||||||
|
@ -12,6 +12,7 @@ export default SingleSelectComponent.extend({
|
|||||||
caretUpIcon: "caret-up",
|
caretUpIcon: "caret-up",
|
||||||
caretDownIcon: "caret-down",
|
caretDownIcon: "caret-down",
|
||||||
showCaret: false,
|
showCaret: false,
|
||||||
|
customStyle: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
modifyComponentForRow() {
|
modifyComponentForRow() {
|
||||||
|
@ -23,10 +23,8 @@ export default MultiSelectComponent.extend(TagsMixin, {
|
|||||||
termMatchesForbidden: false,
|
termMatchesForbidden: false,
|
||||||
categoryId: null,
|
categoryId: null,
|
||||||
everyTag: false,
|
everyTag: false,
|
||||||
none: "tagging.choose_for_topic",
|
|
||||||
closeOnChange: false,
|
closeOnChange: false,
|
||||||
maximum: "maximumSelectedTags",
|
maximum: "maxTagsPerTopic",
|
||||||
minimum: "minimumSelectedTags",
|
|
||||||
autoInsertNoneItem: false,
|
autoInsertNoneItem: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -38,31 +36,20 @@ export default MultiSelectComponent.extend(TagsMixin, {
|
|||||||
return "tag-row";
|
return "tag-row";
|
||||||
},
|
},
|
||||||
|
|
||||||
allowAnyTag: or("allowCreate", "site.can_create_tag"),
|
modifyNoSelection() {
|
||||||
|
if (this.selectKit.options.minimum > 0) {
|
||||||
maximumSelectedTags: computed(function () {
|
return this.defaultItem(
|
||||||
return parseInt(
|
null,
|
||||||
this.options.limit ||
|
I18n.t("tagging.choose_for_topic_required", {
|
||||||
this.selectKit.options.maximum ||
|
count: this.selectKit.options.minimum,
|
||||||
this.maxTagsPerTopic,
|
})
|
||||||
10
|
);
|
||||||
);
|
} else {
|
||||||
}),
|
return this.defaultItem(null, I18n.t("tagging.choose_for_topic"));
|
||||||
|
|
||||||
minimumSelectedTags: computed(function () {
|
|
||||||
if (
|
|
||||||
this.selectKit.options.minimum ||
|
|
||||||
this.selectKit.options.requiredTagGroups
|
|
||||||
) {
|
|
||||||
const minimum = parseInt(this.selectKit.options.minimum, 10);
|
|
||||||
if (minimum > 0) {
|
|
||||||
return this.defaultItem(
|
|
||||||
null,
|
|
||||||
I18n.t("select_kit.min_content_not_reached", { count: minimum })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
|
|
||||||
|
allowAnyTag: or("allowCreate", "site.can_create_tag"),
|
||||||
|
|
||||||
caretIcon: computed("value.[]", "content.[]", function () {
|
caretIcon: computed("value.[]", "content.[]", function () {
|
||||||
const maximum = this.selectKit.options.maximum;
|
const maximum = this.selectKit.options.maximum;
|
||||||
|
@ -124,35 +124,40 @@ export default SelectKitComponent.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedContent: computed("value.[]", "content.[]", function () {
|
selectedContent: computed(
|
||||||
const value = makeArray(this.value).map((v) =>
|
"value.[]",
|
||||||
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
|
"content.[]",
|
||||||
);
|
"selectKit.noneItem",
|
||||||
|
function () {
|
||||||
|
const value = makeArray(this.value).map((v) =>
|
||||||
|
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
|
||||||
|
);
|
||||||
|
|
||||||
if (value.length) {
|
if (value.length) {
|
||||||
let content = [];
|
let content = [];
|
||||||
|
|
||||||
value.forEach((v) => {
|
value.forEach((v) => {
|
||||||
if (this.selectKit.valueProperty) {
|
if (this.selectKit.valueProperty) {
|
||||||
const c = makeArray(this.content).findBy(
|
const c = makeArray(this.content).findBy(
|
||||||
this.selectKit.valueProperty,
|
this.selectKit.valueProperty,
|
||||||
v
|
v
|
||||||
);
|
);
|
||||||
if (c) {
|
if (c) {
|
||||||
content.push(c);
|
content.push(c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (makeArray(this.content).includes(v)) {
|
||||||
|
content.push(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
if (makeArray(this.content).includes(v)) {
|
|
||||||
content.push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.selectKit.modifySelection(content);
|
return this.selectKit.modifySelection(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.selectKit.noneItem;
|
||||||
}
|
}
|
||||||
|
),
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
|
|
||||||
_onKeydown(event) {
|
_onKeydown(event) {
|
||||||
if (
|
if (
|
||||||
|
@ -209,8 +209,12 @@ export default Component.extend(
|
|||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
const computedOptions = {};
|
|
||||||
Object.keys(this.selectKitOptions).forEach((key) => {
|
Object.keys(this.selectKitOptions).forEach((key) => {
|
||||||
|
if (isPresent(this.options[key])) {
|
||||||
|
this.selectKit.options.set(key, this.options[key]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const value = this.selectKitOptions[key];
|
const value = this.selectKitOptions[key];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -219,9 +223,9 @@ export default Component.extend(
|
|||||||
key === "componentForCollection"
|
key === "componentForCollection"
|
||||||
) {
|
) {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
computedOptions[key] = () => value;
|
this.selectKit.options.set(key, () => value);
|
||||||
} else {
|
} else {
|
||||||
computedOptions[key] = bind(this, value);
|
this.selectKit.options.set(key, bind(this, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -234,15 +238,13 @@ export default Component.extend(
|
|||||||
) {
|
) {
|
||||||
const computedValue = get(this, value);
|
const computedValue = get(this, value);
|
||||||
if (typeof computedValue !== "function") {
|
if (typeof computedValue !== "function") {
|
||||||
computedOptions[key] = get(this, value);
|
this.selectKit.options.set(key, computedValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
computedOptions[key] = value;
|
|
||||||
|
this.selectKit.options.set(key, value);
|
||||||
});
|
});
|
||||||
this.selectKit.options.setProperties(
|
|
||||||
Object.assign(computedOptions, this.options || {})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.selectKit.setProperties({
|
this.selectKit.setProperties({
|
||||||
hasSelection: !isEmpty(this.value),
|
hasSelection: !isEmpty(this.value),
|
||||||
@ -264,6 +266,7 @@ export default Component.extend(
|
|||||||
},
|
},
|
||||||
|
|
||||||
selectKitOptions: {
|
selectKitOptions: {
|
||||||
|
allowAny: false,
|
||||||
showFullTitle: true,
|
showFullTitle: true,
|
||||||
none: null,
|
none: null,
|
||||||
translatedNone: null,
|
translatedNone: null,
|
||||||
@ -277,7 +280,6 @@ export default Component.extend(
|
|||||||
maximum: null,
|
maximum: null,
|
||||||
maximumLabel: null,
|
maximumLabel: null,
|
||||||
minimum: null,
|
minimum: null,
|
||||||
minimumLabel: null,
|
|
||||||
autoInsertNoneItem: true,
|
autoInsertNoneItem: true,
|
||||||
closeOnChange: true,
|
closeOnChange: true,
|
||||||
limitMatches: null,
|
limitMatches: null,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
id=(concat selectKit.uniqueID "-filter")
|
id=(concat selectKit.uniqueID "-filter")
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{#if selectedContent}}
|
{{#if selectedContent.length}}
|
||||||
<div class="selected-content">
|
<div class="selected-content">
|
||||||
{{#each selectedContent as |item|}}
|
{{#each selectedContent as |item|}}
|
||||||
{{component selectKit.options.selectedChoiceComponent
|
{{component selectKit.options.selectedChoiceComponent
|
||||||
|
@ -3768,6 +3768,9 @@ en:
|
|||||||
changed: "tags changed:"
|
changed: "tags changed:"
|
||||||
tags: "Tags"
|
tags: "Tags"
|
||||||
choose_for_topic: "optional tags"
|
choose_for_topic: "optional tags"
|
||||||
|
choose_for_topic_required:
|
||||||
|
one: "select at least %{count} tag..."
|
||||||
|
other: "select at least %{count} tags..."
|
||||||
info: "Info"
|
info: "Info"
|
||||||
default_info: "This tag isn't restricted to any categories, and has no synonyms. To add restrictions, put this tag in a <a href=%{basePath}/tag_groups>tag group</a>."
|
default_info: "This tag isn't restricted to any categories, and has no synonyms. To add restrictions, put this tag in a <a href=%{basePath}/tag_groups>tag group</a>."
|
||||||
category_restricted: "This tag is restricted to categories you don't have permission to access."
|
category_restricted: "This tag is restricted to categories you don't have permission to access."
|
||||||
|
Loading…
Reference in New Issue
Block a user