diff --git a/app/models/tag.rb b/app/models/tag.rb index fabae29a1e8..5e3d6809205 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -26,12 +26,7 @@ class Tag < ActiveRecord::Base def self.top_tags(limit_arg: nil, category: nil) limit = limit_arg || SiteSetting.max_tags_in_filter_list - tags = - if category - self.category_tags_by_count_query(category, limit: limit) - else - self.tags_by_count_query(limit: limit) - end + tags = DiscourseTagging.filter_allowed_tags(tags_by_count_query(limit: limit), nil, category: category) tags.count.map {|name, _| name} end diff --git a/lib/discourse_tagging.rb b/lib/discourse_tagging.rb index 855b27f178f..044935f5bef 100644 --- a/lib/discourse_tagging.rb +++ b/lib/discourse_tagging.rb @@ -61,47 +61,46 @@ module DiscourseTagging query = query.where('tags.name like ?', "%#{term}%") end + # Filters for category-specific tags: + 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 IN (?))", + category.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 + elsif opts[:for_input] || category + # exclude tags that are restricted to other categories + if CategoryTag.exists? + query = query.where("tags.id NOT IN (SELECT tag_id FROM category_tags)") + end + + 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 IN (?))", tag_group_ids) + end + end + if opts[:for_input] selected_tag_ids = opts[:selected_tags] ? Tag.where(name: opts[:selected_tags]).pluck(:id) : [] - unless guardian.is_staff? + unless guardian.nil? || 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? end - # Filters for category-specific tags: - - 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 IN (?))", - category.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 - if CategoryTag.exists? - query = query.where("tags.id NOT IN (SELECT tag_id FROM category_tags)") - end - - 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 IN (?))", tag_group_ids) - end - end - # exclude tag groups that have a parent tag which is missing from selected_tags select_sql = <<-SQL diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 22cda4a4b99..1a42a27cea0 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -1,6 +1,20 @@ require 'rails_helper' describe Tag do + def make_some_tags(count: 3, tag_a_topic: false) + @tags = [] + if tag_a_topic + count.times { |i| @tags << Fabricate(:tag, topics: [Fabricate(:topic)]) } + else + count.times { |i| @tags << Fabricate(:tag) } + end + end + + before do + SiteSetting.tagging_enabled = true + SiteSetting.min_trust_level_to_tag_topics = 0 + end + describe '#tags_by_count_query' do it "returns empty hash if nothing is tagged" do expect(described_class.tags_by_count_query.count).to eq({}) @@ -9,9 +23,8 @@ describe Tag do context "with some tagged topics" do before do @topics = [] - @tags = [] 3.times { @topics << Fabricate(:topic) } - 2.times { @tags << Fabricate(:tag) } + make_some_tags(count: 2) @topics[0].tags << @tags[0] @topics[0].tags << @tags[1] @topics[1].tags << @tags[0] @@ -29,4 +42,40 @@ describe Tag do end end end + + describe '#top_tags' do + it "returns nothing if nothing has been tagged" do + make_some_tags(tag_a_topic: false) + expect(described_class.top_tags.sort).to be_empty + end + + it "can return all tags" do + make_some_tags(tag_a_topic: true) + expect(described_class.top_tags.sort).to eq(@tags.map(&:name).sort) + end + + context "with category-specific tags" do + before do + make_some_tags(count: 3) + @category1 = Fabricate(:category, tags: [@tags[0]]) # only one tag allowed in this category + @category2 = Fabricate(:category) + @topics = [] + @topics << Fabricate(:topic, category: @category1, tags: [@tags[0]]) + @topics << Fabricate(:topic, category: @category2, tags: [@tags[1], @tags[2]]) + @topics << Fabricate(:topic, tags: [@tags[2]]) # uncategorized + end + + it "for category with restricted tags, lists those tags" do + expect(described_class.top_tags(category: @category1)).to eq([@tags[0].name]) + end + + it "for category without tags, lists allowed tags" do + expect(described_class.top_tags(category: @category2).sort).to eq([@tags[1].name, @tags[2].name].sort) + end + + it "for no category arg, lists all tags" do + expect(described_class.top_tags.sort).to eq([@tags[0].name, @tags[1].name, @tags[2].name].sort) + end + end + end end diff --git a/spec/models/topic_list_spec.rb b/spec/models/topic_list_spec.rb index baedb5c3561..38a13515d97 100644 --- a/spec/models/topic_list_spec.rb +++ b/spec/models/topic_list_spec.rb @@ -38,25 +38,34 @@ describe TopicList do end describe '#tags' do - let(:tag) { Fabricate(:tag, topics: [topic]) } - let(:other_tag) { Fabricate(:tag, topics: [topic]) } - it 'should return the right tags' do + tag = Fabricate(:tag, topics: [topic]) + other_tag = Fabricate(:tag, topics: [topic], name: "use-anywhere") output = [tag.name, other_tag.name] expect(topic_list.tags.sort).to eq(output.sort) end - describe 'when topic list is filtered by category' do - let(:category) { Fabricate(:category) } - let(:topic) { Fabricate(:topic, category: category) } - let(:tag) { Fabricate(:tag, topics: [topic], categories: [category]) } + describe 'when there are tags restricted to a category' do + let!(:category) { Fabricate(:category) } + let!(:topic) { Fabricate(:topic, category: category) } + let!(:other_topic) { Fabricate(:topic) } # uncategorized + let!(:tag) { Fabricate(:tag, topics: [topic], categories: [category], name: "category-tag") } + let!(:other_tag) { Fabricate(:tag, topics: [topic], name: "use-anywhere") } let(:topic_list) { TopicList.new('latest', topic.user, [topic], { category: category.id, category_id: category.id }) } it 'should only return tags allowed in the category' do - other_tag - output = [tag.name] + expect(topic_list.tags).to eq([tag.name]) + end - expect(topic_list.tags).to eq(output) + it "with no category, should return all tags" do + expect(TopicList.new('latest', other_topic.user, [other_topic]).tags.sort).to eq([tag.name, other_tag.name].sort) + end + + it "with another category with no tags, should return exclude tags restricted to other categories" do + other_category = Fabricate(:category) + topic3 = Fabricate(:topic, category: other_category) + list = TopicList.new('latest', topic3.user, [topic3], { category: other_category.id, category_id: other_category.id }) + expect(list.tags).to eq([other_tag.name]) end end end