+
{{#if renderTags}}
{{#each tags as |t|}}
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index a476c204db7..e0b945931d5 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -57,7 +57,7 @@ class TagsController < ::ApplicationController
@list.more_topics_url = list_by_tag_path(tag_id: @tag_id, page: page + 1)
@rss = "tag"
- if @list.topics.size == 0 && !Tag.where(name: @tag_id).exists?
+ if @list.topics.size == 0 && params[:tag_id] != 'none' && !Tag.where(name: @tag_id).exists?
raise Discourse::NotFound
else
respond_with_list(@list)
@@ -203,7 +203,6 @@ class TagsController < ::ApplicationController
topic_ids: param_to_integer_list(:topic_ids),
exclude_category_ids: params[:exclude_category_ids],
category: params[:category],
- tags: [params[:tag_id]],
order: params[:order],
ascending: params[:ascending],
min_posts: params[:min_posts],
@@ -217,6 +216,12 @@ class TagsController < ::ApplicationController
options[:no_subcategories] = true if params[:no_subcategories] == 'true'
options[:slow_platform] = true if slow_platform?
+ if params[:tag_id] == 'none'
+ options[:no_tags] = true
+ else
+ options[:tags] = [params[:tag_id]]
+ end
+
options
end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 3cb67e0c85d..56876ff59e0 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2123,6 +2123,7 @@ en:
tagging:
all_tags: "All Tags"
selector_all_tags: "all tags"
+ selector_no_tags: "no tags"
changed: "tags changed:"
tags: "Tags"
choose_for_topic: "choose optional tags for this topic"
@@ -2139,6 +2140,8 @@ en:
filters:
without_category: "%{filter} %{tag} topics"
with_category: "%{filter} %{tag} topics in %{category}"
+ untagged_without_category: "%{filter} untagged topics"
+ untagged_with_category: "%{filter} untagged topics in %{category}"
notifications:
watching:
diff --git a/lib/topic_query.rb b/lib/topic_query.rb
index ed4aecf8181..49cef6da060 100644
--- a/lib/topic_query.rb
+++ b/lib/topic_query.rb
@@ -20,6 +20,7 @@ class TopicQuery
visible
category
tags
+ no_tags
order
ascending
no_subcategories
@@ -465,6 +466,9 @@ class TopicQuery
else
result = result.where("tags.name in (?)", @options[:tags])
end
+ elsif @options[:no_tags]
+ # the following will do: ("topics"."id" NOT IN (SELECT DISTINCT "topic_tags"."topic_id" FROM "topic_tags"))
+ result = result.where.not(:id => TopicTag.select(:topic_id).uniq)
end
end
diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb
index 0b9b19f8547..1c6e63fceff 100644
--- a/spec/components/topic_query_spec.rb
+++ b/spec/components/topic_query_spec.rb
@@ -122,25 +122,32 @@ describe TopicQuery do
SiteSetting.tagging_enabled = true
end
- it "returns topics with the tag when filtered to it" do
- tagged_topic1 = Fabricate(:topic, {tags: [tag]})
- tagged_topic2 = Fabricate(:topic, {tags: [other_tag]})
- tagged_topic3 = Fabricate(:topic, {tags: [tag, other_tag]})
- no_tags_topic = Fabricate(:topic)
+ context "no category filter" do
+ # create some topics before each test:
+ let!(:tagged_topic1) { Fabricate(:topic, {tags: [tag]}) }
+ let!(:tagged_topic2) { Fabricate(:topic, {tags: [other_tag]}) }
+ let!(:tagged_topic3) { Fabricate(:topic, {tags: [tag, other_tag]}) }
+ let!(:no_tags_topic) { Fabricate(:topic) }
- expect(TopicQuery.new(moderator, tags: [tag.name]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic3.id].sort)
- expect(TopicQuery.new(moderator, tags: [tag.id]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic3.id].sort)
+ it "returns topics with the tag when filtered to it" do
+ expect(TopicQuery.new(moderator, tags: [tag.name]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic3.id].sort)
+ expect(TopicQuery.new(moderator, tags: [tag.id]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic3.id].sort)
- two_tag_topic = TopicQuery.new(moderator, tags: [tag.name]).list_latest.topics.find { |t| t.id == tagged_topic3.id }
- expect(two_tag_topic.tags.size).to eq(2)
+ two_tag_topic = TopicQuery.new(moderator, tags: [tag.name]).list_latest.topics.find { |t| t.id == tagged_topic3.id }
+ expect(two_tag_topic.tags.size).to eq(2)
- # topics with ANY of the given tags:
- expect(TopicQuery.new(moderator, tags: [tag.name, other_tag.name]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic2.id, tagged_topic3.id].sort)
- expect(TopicQuery.new(moderator, tags: [tag.id, other_tag.id]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic2.id, tagged_topic3.id].sort)
+ # topics with ANY of the given tags:
+ expect(TopicQuery.new(moderator, tags: [tag.name, other_tag.name]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic2.id, tagged_topic3.id].sort)
+ expect(TopicQuery.new(moderator, tags: [tag.id, other_tag.id]).list_latest.topics.map(&:id).sort).to eq([tagged_topic1.id, tagged_topic2.id, tagged_topic3.id].sort)
- # TODO: topics with ALL of the given tags:
- # expect(TopicQuery.new(moderator, tags: [tag.name, other_tag.name]).list_latest.topics.map(&:id)).to eq([tagged_topic3.id].sort)
- # expect(TopicQuery.new(moderator, tags: [tag.id, other_tag.id]).list_latest.topics.map(&:id)).to eq([tagged_topic3.id].sort)
+ # TODO: topics with ALL of the given tags:
+ # expect(TopicQuery.new(moderator, tags: [tag.name, other_tag.name]).list_latest.topics.map(&:id)).to eq([tagged_topic3.id].sort)
+ # expect(TopicQuery.new(moderator, tags: [tag.id, other_tag.id]).list_latest.topics.map(&:id)).to eq([tagged_topic3.id].sort)
+ end
+
+ it "can return topics with no tags" do
+ expect(TopicQuery.new(moderator, no_tags: true).list_latest.topics.map(&:id)).to eq([no_tags_topic.id])
+ end
end
context "and categories too" do