FEATURE: support group querying in created-by filter (/filter) (#35692)

Currently, there is no way to query topics `created by anyone in this
(input-group)` , with this PR we address that.

It is an overload – similar to what assign does:


https://github.com/discourse/discourse/blob/a54e3208cb1e331346d17c1687f593dcdd9eb583/plugins/discourse-assign/plugin.rb#L327-L345

I used the same tests as in
https://github.com/discourse/discourse/pull/35631
This commit is contained in:
Gabriel Grubba
2025-10-29 19:09:17 -03:00
committed by GitHub
parent 6b6042dd6d
commit 353ae07740
3 changed files with 155 additions and 11 deletions
+3 -3
View File
@@ -2893,9 +2893,9 @@ en:
activity_after: "Show topics with last activity after a date (YYYY-MM-DD or days ago)"
created_before: "Show topics created before a date (YYYY-MM-DD or days ago)"
created_after: "Show topics created after a date (YYYY-MM-DD or days ago)"
created_by: "Show topics created by a specific user"
created_by_user: "Show topics created by username (without @)"
created_by_multiple: "Show topics created by any of the specified users (comma-separated)"
created_by: "Show topics created by a specific user or group"
created_by_user: "Show topics created by username or group (without @)"
created_by_multiple: "Show topics created by any of the specified users or groups (comma-separated)"
latest_post_before: "Show topics with last post before a date (YYYY-MM-DD or days ago)"
latest_post_after: "Show topics with last post after a date (YYYY-MM-DD or days ago)"
likes_min: "Show topics with at least this many likes"
+29 -8
View File
@@ -52,7 +52,7 @@ class TopicsFilter
when "created-before"
filter_by_created(before: filter_values)
when "created-by"
filter_created_by_user(usernames: filter_values.flat_map { |value| value.split(",") })
filter_created_by(names: filter_values.flat_map { |value| value.split(",") })
when "in"
filter_in(values: filter_values)
when "latest-post-after"
@@ -197,7 +197,7 @@ class TopicsFilter
{
name: "created-by:",
description: I18n.t("filter.description.created_by"),
type: "username",
type: "username_group_list",
delimiters: [{ name: ",", description: I18n.t("filter.description.created_by_multiple") }],
},
{
@@ -659,12 +659,33 @@ class TopicsFilter
SQL
end
def filter_created_by_user(usernames:)
@scope =
@scope.joins(:user).where(
"users.username_lower IN (:usernames)",
usernames: usernames.map(&:downcase),
)
def filter_created_by(names:)
if names.include?("me") && @guardian.authenticated?
names = names.map { |n| n == "me" ? @guardian.user.username_lower : n }
end
if (user_ids = User.where("username_lower IN (?)", names.map(&:downcase)).pluck(:id)) &&
user_ids.any?
@scope = @scope.joins(:user).where(user_id: user_ids)
return
end
if (
group_ids =
Group
.visible_groups(@guardian.user)
.where("lower(name) IN (?)", names.map(&:downcase))
.pluck(:id)
) && group_ids.any?
@scope =
@scope
.joins(:user)
.joins("INNER JOIN group_users ON group_users.user_id = users.id")
.where("group_users.group_id IN (?)", group_ids)
.distinct(:id)
return
end
@scope = @scope.none
end
def apply_custom_filter!(scope:, filter_name:, values:)
+123
View File
@@ -1699,6 +1699,129 @@ RSpec.describe TopicsFilter do
).to eq([])
end
end
describe "when query string is `created-by:me`" do
it "should return the topics created by the current user" do
expect(
TopicsFilter
.new(guardian: Guardian.new(user))
.filter_from_query_string("created-by:me")
.pluck(:id),
).to contain_exactly(topic_by_user.id, topic2_by_user.id)
end
it "should not return any topics when there is no current user" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:me")
.pluck(:id),
).to eq([])
end
end
end
describe "when filtering by topic creator's group" do
fab!(:group1) { Fabricate(:group, name: "group1") }
fab!(:group2) { Fabricate(:group, name: "group2") }
fab!(:user_in_group1) { Fabricate(:user).tap { |u| group1.add(u) } }
fab!(:user_in_group2) { Fabricate(:user).tap { |u| group2.add(u) } }
fab!(:user_in_both_groups) do
Fabricate(:user).tap do |u|
group1.add(u)
group2.add(u)
end
end
fab!(:topic_by_group1_user) { Fabricate(:topic, user: user_in_group1) }
fab!(:topic_by_group2_user) { Fabricate(:topic, user: user_in_group2) }
fab!(:topic_by_both_groups_user) { Fabricate(:topic, user: user_in_both_groups) }
describe "when query string is `created-by:group1`" do
it "should return topics created by users in the specified group" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:group1")
.pluck(:id),
).to contain_exactly(topic_by_group1_user.id, topic_by_both_groups_user.id)
end
end
describe "when query string is `created-by:group2`" do
it "should return topics created by users in the specified group" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:group2")
.pluck(:id),
).to contain_exactly(topic_by_group2_user.id, topic_by_both_groups_user.id)
end
end
describe "when query string is `created-by:group1,group2`" do
it "should return topics created by users in any of the specified groups" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:group1,group2")
.pluck(:id),
).to contain_exactly(
topic_by_group1_user.id,
topic_by_group2_user.id,
topic_by_both_groups_user.id,
)
end
end
describe "when query string is `created-by:invalid`" do
it "should not return any topics" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:invalid")
.pluck(:id),
).to eq([])
end
end
describe "when query string is `created-by:group1,invalid`" do
it "should only return topics created by users in the valid group" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:group1,invalid")
.pluck(:id),
).to contain_exactly(topic_by_group1_user.id, topic_by_both_groups_user.id)
end
end
describe "with group visibility restrictions" do
fab!(:private_group) do
Fabricate(:group, visibility_level: Group.visibility_levels[:members])
end
fab!(:user_in_private_group) { Fabricate(:user).tap { |u| private_group.add(u) } }
fab!(:topic_by_private_group_user) { Fabricate(:topic, user: user_in_private_group) }
it "should not return topics when user cannot see the group" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{private_group.name}")
.pluck(:id),
).to eq([])
end
it "should return topics when user is a member of the private group" do
private_group.add(user)
expect(
TopicsFilter
.new(guardian: Guardian.new(user))
.filter_from_query_string("created-by:#{private_group.name}")
.pluck(:id),
).to contain_exactly(topic_by_private_group_user.id)
end
end
end
shared_examples "filtering for topics by counts" do |filter|