diff --git a/app/models/query.rb b/app/models/query.rb index b1f784528..96e99ef94 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -195,6 +195,9 @@ class Query < ActiveRecord::Base end @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty? + + group_values = Group.all.collect {|g| [g.name, g.id] } + @available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty? if User.current.logged? @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] } @@ -432,6 +435,22 @@ class Query < ActiveRecord::Base db_field = 'user_id' sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " sql << sql_for_field(field, '=', v, db_table, db_field) + ')' + elsif field == "member_of_group" # named field + if operator == '*' # Any group + groups = Group.all + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + operator = '=' # Override the operator since we want to find by assigned_to + elsif operator == "!*" + groups = Group.all + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + operator = '!' # Override the operator since we want to find by assigned_to + else + groups = Group.find_all_by_id(v) + members_of_groups = groups.collect(&:user_ids).flatten.compact.collect(&:to_s) + end + + sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')' + else # regular field db_table = Issue.table_name diff --git a/config/locales/en.yml b/config/locales/en.yml index 555043a8f..ba7a2da23 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -293,6 +293,7 @@ en: field_group_by: Group results by field_sharing: Sharing field_parent_issue: Parent task + field_member_of_group: Member of Group setting_app_title: Application title setting_app_subtitle: Application subtitle diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 12ab5d932..acab56ffd 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -48,6 +48,16 @@ class QueryTest < ActiveSupport::TestCase :conditions => query.statement end + def assert_find_issues_with_query_is_successful(query) + assert_nothing_raised do + find_issues_with_query(query) + end + end + + def assert_query_statement_includes(query, condition) + assert query.statement.include?(condition), "Query statement condition not found in: #{query.statement}" + end + def test_query_should_allow_shared_versions_for_a_project_query subproject_version = Version.find(4) query = Query.new(:project => Project.find(1), :name => '_') @@ -362,11 +372,87 @@ class QueryTest < ActiveSupport::TestCase end context "#available_filters" do + setup do + @query = Query.new(:name => "_") + end + should "include users of visible projects in cross-project view" do - query = Query.new(:name => "_") - users = query.available_filters["assigned_to_id"] + users = @query.available_filters["assigned_to_id"] assert_not_nil users assert users[:values].map{|u|u[1]}.include?("3") end + + context "'member_of_group' filter" do + should "be present" do + assert @query.available_filters.keys.include?("member_of_group") + end + + should "be an optional list" do + assert_equal :list_optional, @query.available_filters["member_of_group"][:type] + end + + should "have a list of the groups as values" do + Group.destroy_all # No fixtures + group1 = Group.generate!.reload + group2 = Group.generate!.reload + + expected_group_list = [ + [group1.name, group1.id], + [group2.name, group2.id] + ] + assert_equal expected_group_list, @query.available_filters["member_of_group"][:values] + end + + end + end + + context "#statement" do + context "with 'member_of_group' filter" do + setup do + Group.destroy_all # No fixtures + @user_in_group = User.generate! + @second_user_in_group = User.generate! + @user_in_group2 = User.generate! + @user_not_in_group = User.generate! + + @group = Group.generate!.reload + @group.users << @user_in_group + @group.users << @second_user_in_group + + @group2 = Group.generate!.reload + @group2.users << @user_in_group2 + + end + + should "search assigned to for users in the group" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '=', [@group.id.to_s]) + + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}')" + assert_find_issues_with_query_is_successful @query + end + + should "search not assigned to any group member (none)" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '!*', ['']) + + # Users not in a group + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')" + assert_find_issues_with_query_is_successful @query + + end + + should "search assigned to any group member (all)" do + @query = Query.new(:name => '_') + @query.add_filter('member_of_group', '*', ['']) + + # Only users in a group + assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')" + assert_find_issues_with_query_is_successful @query + + end + end + end + end