mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: tag group options: limit usage of one tag per group, tags in a group can't be used unless a prerequisite tag is used
This commit is contained in:
@@ -36,12 +36,19 @@ export default Ember.TextField.extend({
|
||||
const site = this.site,
|
||||
self = this,
|
||||
filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
|
||||
var limit = this.siteSettings.max_tags_per_topic;
|
||||
|
||||
if (this.get('unlimitedTagCount')) {
|
||||
limit = null;
|
||||
} else if (this.get('limit')) {
|
||||
limit = parseInt(this.get('limit'));
|
||||
}
|
||||
|
||||
this.$().select2({
|
||||
tags: true,
|
||||
placeholder: I18n.t(this.get('placeholderKey') || 'tagging.choose_for_topic'),
|
||||
maximumInputLength: this.siteSettings.max_tag_length,
|
||||
maximumSelectionSize: self.get('unlimitedTagCount') ? null : this.siteSettings.max_tags_per_topic,
|
||||
maximumSelectionSize: limit,
|
||||
initSelection(element, callback) {
|
||||
const data = [];
|
||||
|
||||
@@ -92,7 +99,12 @@ export default Ember.TextField.extend({
|
||||
url: Discourse.getURL("/tags/filter/search"),
|
||||
dataType: 'json',
|
||||
data: function (term) {
|
||||
const d = { q: term, limit: self.siteSettings.max_tag_search_results, categoryId: self.get('categoryId') };
|
||||
const d = {
|
||||
q: term,
|
||||
limit: self.siteSettings.max_tag_search_results,
|
||||
categoryId: self.get('categoryId'),
|
||||
selected_tags: self.get('tags')
|
||||
};
|
||||
if (!self.get('everyTag')) {
|
||||
d.filterForInput = true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ const TagGroup = RestModel.extend({
|
||||
return Discourse.ajax(url, {
|
||||
data: {
|
||||
name: this.get('name'),
|
||||
tag_names: this.get('tag_names')
|
||||
tag_names: this.get('tag_names'),
|
||||
parent_tag_name: this.get('parent_tag_name') ? this.get('parent_tag_name') : undefined,
|
||||
one_per_topic: this.get('one_per_topic')
|
||||
},
|
||||
type: this.get('id') ? 'PUT' : 'POST'
|
||||
}).then(function(result) {
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
<div class="tag-group-content">
|
||||
<h1>{{text-field value=model.name}}</h1>
|
||||
<br/>
|
||||
<div class="group-tags-list">
|
||||
<label>{{i18n 'tagging.groups.tags_label'}}</label>
|
||||
<br/>
|
||||
{{tag-chooser tags=model.tag_names everyTag="false" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<br/>
|
||||
<section class="group-tags-list">
|
||||
<label>{{i18n 'tagging.groups.tags_label'}}</label><br/>
|
||||
{{tag-chooser tags=model.tag_names everyTag="true" unlimitedTagCount="true"}}
|
||||
</section>
|
||||
|
||||
<section class="parent-tag-section">
|
||||
<label>{{i18n 'tagging.groups.parent_tag_label'}}</label>
|
||||
{{tag-chooser tags=model.parent_tag_name everyTag="true" limit="1" placeholderKey="tagging.groups.parent_tag_placeholder"}}
|
||||
<span class="description">{{i18n 'tagging.groups.parent_tag_description'}}</span>
|
||||
</section>
|
||||
|
||||
<section class="group-one-per-topic">
|
||||
<label>
|
||||
{{input type="checkbox" checked=model.one_per_topic name="onepertopic"}}
|
||||
{{i18n 'tagging.groups.one_per_topic_label'}}
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'tagging.groups.save'}}</button>
|
||||
<button {{action "destroy"}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'tagging.groups.delete'}}</button>
|
||||
<button {{action "destroy"}} disabled={{model.disableSave}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'tagging.groups.delete'}}</button>
|
||||
<span class="saving {{unless model.savingStatus 'hidden'}}">{{model.savingStatus}}</span>
|
||||
</div>
|
||||
@@ -222,6 +222,14 @@ header .discourse-tag {color: $tag-color !important; }
|
||||
.tag-group-content {
|
||||
width: 75%;
|
||||
float: right;
|
||||
section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
font-size: 1em;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.group-tags-list .tag-chooser {
|
||||
height: 150px !important;
|
||||
@@ -233,4 +241,10 @@ header .discourse-tag {color: $tag-color !important; }
|
||||
.saving {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.parent-tag-section {
|
||||
.tag-chooser {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,10 @@ class TagGroupsController < ApplicationController
|
||||
end
|
||||
|
||||
def tag_groups_params
|
||||
params[:tag_names] ||= []
|
||||
params.permit(:id, :name, :tag_names => [])
|
||||
result = params.permit(:id, :name, :one_per_topic, :tag_names => [], :parent_tag_name => [])
|
||||
result[:tag_names] ||= []
|
||||
result[:parent_tag_name] ||= []
|
||||
result[:one_per_topic] = (params[:one_per_topic] == "true")
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
@@ -117,7 +117,12 @@ class TagsController < ::ApplicationController
|
||||
tags_with_counts = DiscourseTagging.filter_allowed_tags(
|
||||
self.class.tags_by_count(guardian, params.slice(:limit)),
|
||||
guardian,
|
||||
{ for_input: params[:filterForInput], term: params[:q], category: category }
|
||||
{
|
||||
for_input: params[:filterForInput],
|
||||
term: params[:q],
|
||||
category: category,
|
||||
selected_tags: params[:selected_tags]
|
||||
}
|
||||
)
|
||||
|
||||
tags = tags_with_counts.count.map {|t, c| { id: t, text: t, count: c } }
|
||||
@@ -125,7 +130,7 @@ class TagsController < ::ApplicationController
|
||||
unused_tags = DiscourseTagging.filter_allowed_tags(
|
||||
Tag.where(topic_count: 0),
|
||||
guardian,
|
||||
{ for_input: params[:filterForInput], term: params[:q], category: category }
|
||||
{ for_input: params[:filterForInput], term: params[:q], category: category, selected_tags: params[:selected_tags] }
|
||||
)
|
||||
|
||||
unused_tags.each do |t|
|
||||
|
||||
@@ -6,7 +6,19 @@ class TagGroup < ActiveRecord::Base
|
||||
has_many :category_tag_groups, dependent: :destroy
|
||||
has_many :categories, through: :category_tag_groups
|
||||
|
||||
belongs_to :parent_tag, class_name: 'Tag'
|
||||
|
||||
def tag_names=(tag_names_arg)
|
||||
DiscourseTagging.add_or_create_tags_by_name(self, tag_names_arg)
|
||||
end
|
||||
|
||||
def parent_tag_name=(tag_names_arg)
|
||||
if tag_names_arg.empty?
|
||||
self.parent_tag = nil
|
||||
else
|
||||
if tag_name = DiscourseTagging.tags_for_saving(tag_names_arg, Guardian.new(Discourse.system_user)).first
|
||||
self.parent_tag = Tag.find_by_name(tag_name) || Tag.create(name: tag_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class TagGroupMembership < ActiveRecord::Base
|
||||
belongs_to :tag
|
||||
belongs_to :tag_group, counter_cache: "tag_count"
|
||||
belongs_to :tag_group, counter_cache: "tag_count" # TODO: remove counter cache
|
||||
end
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
class TagGroupSerializer < ApplicationSerializer
|
||||
attributes :id, :name, :tag_names
|
||||
attributes :id, :name, :tag_names, :parent_tag_name, :one_per_topic
|
||||
|
||||
def tag_names
|
||||
object.tags.pluck(:name).sort
|
||||
end
|
||||
|
||||
def parent_tag_name
|
||||
[object.parent_tag.try(:name)].compact
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user