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:
@@ -33,7 +33,7 @@ module DiscourseTagging
|
||||
|
||||
if tag_names.present?
|
||||
category = topic.category
|
||||
tags = filter_allowed_tags(Tag.where(name: tag_names), guardian, { for_input: true, category: category }).to_a
|
||||
tags = filter_allowed_tags(Tag.where(name: tag_names), guardian, { for_input: true, category: category, selected_tags: tag_names }).to_a
|
||||
|
||||
if tags.size < tag_names.size && (category.nil? || category.tags.count == 0)
|
||||
tag_names.each do |name|
|
||||
@@ -56,8 +56,9 @@ module DiscourseTagging
|
||||
|
||||
# Options:
|
||||
# term: a search term to filter tags by name
|
||||
# for_input: result is for an input field, so only show permitted tags
|
||||
# category: a Category to which the object being tagged belongs
|
||||
# for_input: result is for an input field, so only show permitted tags
|
||||
# selected_tags: an array of tag names that are in the current selection
|
||||
def self.filter_allowed_tags(query, guardian, opts={})
|
||||
term = opts[:term]
|
||||
if term.present?
|
||||
@@ -67,6 +68,8 @@ module DiscourseTagging
|
||||
end
|
||||
|
||||
if opts[:for_input]
|
||||
selected_tag_ids = opts[:selected_tags] ? Tag.where(name: opts[:selected_tags]).pluck(:id) : []
|
||||
|
||||
unless guardian.is_staff?
|
||||
staff_tag_names = SiteSetting.staff_tags.split("|")
|
||||
query = query.where('tags.name NOT IN (?)', staff_tag_names) if staff_tag_names.present?
|
||||
@@ -74,20 +77,24 @@ module DiscourseTagging
|
||||
|
||||
# Filters for category-specific tags:
|
||||
|
||||
if opts[:category] && (opts[:category].tags.count > 0 || opts[:category].tag_groups.count > 0)
|
||||
if opts[:category].tags.count > 0 && opts[:category].tag_groups.count > 0
|
||||
tag_group_ids = opts[:category].tag_groups.pluck(:id)
|
||||
category = opts[:category]
|
||||
|
||||
if category && (category.tags.count > 0 || category.tag_groups.count > 0)
|
||||
if category.tags.count > 0 && category.tag_groups.count > 0
|
||||
tag_group_ids = category.tag_groups.pluck(:id)
|
||||
|
||||
query = query.where(
|
||||
"tags.id IN (SELECT tag_id FROM category_tags WHERE category_id = ?
|
||||
UNION
|
||||
SELECT tag_id FROM tag_group_memberships WHERE tag_group_id = ?)",
|
||||
opts[:category].id, tag_group_ids
|
||||
SELECT tag_id FROM tag_group_memberships WHERE tag_group_id IN (?))",
|
||||
category.id, tag_group_ids
|
||||
)
|
||||
elsif opts[:category].tags.count > 0
|
||||
query = query.where("tags.id IN (SELECT tag_id FROM category_tags WHERE category_id = ?)", opts[:category].id)
|
||||
else # opts[:category].tag_groups.count > 0
|
||||
tag_group_ids = opts[:category].tag_groups.pluck(:id)
|
||||
query = query.where("tags.id IN (SELECT tag_id FROM tag_group_memberships WHERE tag_group_id = ?)", tag_group_ids)
|
||||
elsif category.tags.count > 0
|
||||
query = query.where("tags.id IN (SELECT tag_id FROM category_tags WHERE category_id = ?)", category.id)
|
||||
else # category.tag_groups.count > 0
|
||||
tag_group_ids = category.tag_groups.pluck(:id)
|
||||
|
||||
query = query.where("tags.id IN (SELECT tag_id FROM tag_group_memberships WHERE tag_group_id IN (?))", tag_group_ids)
|
||||
end
|
||||
else
|
||||
# exclude tags that are restricted to other categories
|
||||
@@ -97,7 +104,41 @@ module DiscourseTagging
|
||||
|
||||
if CategoryTagGroup.exists?
|
||||
tag_group_ids = CategoryTagGroup.pluck(:tag_group_id).uniq
|
||||
query = query.where("tags.id NOT IN (SELECT tag_id FROM tag_group_memberships WHERE tag_group_id = ?)", tag_group_ids)
|
||||
query = query.where("tags.id NOT IN (SELECT tag_id FROM tag_group_memberships WHERE tag_group_id IN (?))", tag_group_ids)
|
||||
end
|
||||
end
|
||||
|
||||
# exclude tag groups that have a parent tag which is missing from selected_tags
|
||||
|
||||
select_sql = <<-SQL
|
||||
SELECT tag_id
|
||||
FROM tag_group_memberships tgm
|
||||
INNER JOIN tag_groups tg
|
||||
ON tgm.tag_group_id = tg.id
|
||||
SQL
|
||||
|
||||
if selected_tag_ids.empty?
|
||||
sql = "tags.id NOT IN (#{select_sql} WHERE tg.parent_tag_id IS NOT NULL)"
|
||||
query = query.where(sql)
|
||||
else
|
||||
# One tag per group restriction
|
||||
exclude_group_ids = TagGroup.where(one_per_topic: true)
|
||||
.joins(:tag_group_memberships)
|
||||
.where('tag_group_memberships.tag_id in (?)', selected_tag_ids)
|
||||
.pluck(:id)
|
||||
|
||||
if exclude_group_ids.empty?
|
||||
sql = "tags.id NOT IN (#{select_sql} WHERE tg.parent_tag_id NOT IN (?))"
|
||||
query = query.where(sql, selected_tag_ids)
|
||||
else
|
||||
# It's possible that the selected tags violate some one-tag-per-group restrictions,
|
||||
# so filter them out by picking one from each group.
|
||||
limit_tag_ids = TagGroupMembership.select('distinct on (tag_group_id) tag_id')
|
||||
.where(tag_id: selected_tag_ids)
|
||||
.where(tag_group_id: exclude_group_ids)
|
||||
.map(&:tag_id)
|
||||
sql = "(tags.id NOT IN (#{select_sql} WHERE (tg.parent_tag_id NOT IN (?) OR tg.id in (?))) OR tags.id IN (?))"
|
||||
query = query.where(sql, selected_tag_ids, exclude_group_ids, limit_tag_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user