diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 92459ae00..2ae01f176 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -37,6 +37,8 @@ module QueriesHelper group = query.is_a?(IssueQuery) ? :label_relations : nil elsif %w(member_of_group assigned_to_role).include?(field) group = :field_assigned_to + elsif %w(author_group author_role).include?(field) + group = :field_author elsif field_options[:type] == :date_past || field_options[:type] == :date group = :label_date elsif %w(estimated_hours spent_time).include?(field) diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 4905b1cfb..0d3f05e59 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -180,6 +180,16 @@ class IssueQuery < Query :type => :list_optional, :values => lambda {Role.givable.pluck(:name, :id).map {|name, id| [name, id.to_s]}} ) + add_available_filter( + "author_group", + :type => :list, + :values => lambda {Group.givable.visible.pluck(:name, :id).map {|name, id| [name, id.to_s]}} + ) + add_available_filter( + "author_role", + :type => :list, + :values => lambda {Role.givable.pluck(:name, :id).map {|name, id| [name, id.to_s]}} + ) add_available_filter( "fixed_version_id", :type => :list_optional_with_history, :values => lambda {fixed_version_values} @@ -592,6 +602,36 @@ class IssueQuery < Query end end + def sql_for_author_group_field(field, operator, value) + groups = if value.empty? + Group.givable + else + Group.where(:id => value).to_a + end + + author_groups = groups.inject([]) do |user_ids, group| + user_ids + group.user_ids + [group.id] + end.uniq.compact.sort.collect(&:to_s) + + '(' + sql_for_field("author_id", operator, author_groups, Issue.table_name, "author_id", false) + ')' + end + + def sql_for_author_role_field(field, operator, value) + role_cond = + if value.any? + "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")" + else + "1=0" + end + sw = operator == "!" ? 'NOT' : '' + nl = operator == "!" ? "#{Issue.table_name}.author_id IS NULL OR" : '' + subquery = + "SELECT 1" + + " FROM #{Member.table_name} inner join #{MemberRole.table_name} on members.id = member_roles.member_id" + + " WHERE #{Issue.table_name}.project_id = #{Member.table_name}.project_id AND #{Member.table_name}.user_id = #{Issue.table_name}.author_id AND #{role_cond}" + "(#{nl} #{sw} EXISTS (#{subquery}))" + end + def sql_for_fixed_version_status_field(field, operator, value) where = sql_for_field(field, operator, value, Version.table_name, "status") version_id_scope = project ? project.shared_versions : Version.visible diff --git a/config/locales/en.yml b/config/locales/en.yml index 0fa3b0234..de604ec89 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -376,6 +376,8 @@ en: field_parent_issue_subject: Parent task subject field_member_of_group: "Assignee's group" field_assigned_to_role: "Assignee's role" + field_author_group: "Author's group" + field_author_role: "Author's role" field_text: Text field field_visible: Visible field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 17faf448c..8ab1dddcd 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -334,6 +334,8 @@ ja: field_parent_issue: 親チケット field_member_of_group: 担当者のグループ field_assigned_to_role: 担当者のロール + field_author_group: 作成者のグループ + field_author_role: 作成者のロール field_text: テキスト field_visible: 表示 field_warn_on_leaving_unsaved: データを保存せずにページから移動するときに警告 diff --git a/test/helpers/queries_helper_test.rb b/test/helpers/queries_helper_test.rb index 8c2fd1cb3..029799013 100644 --- a/test/helpers/queries_helper_test.rb +++ b/test/helpers/queries_helper_test.rb @@ -117,4 +117,13 @@ class QueriesHelperTest < Redmine::HelperTest assert_select_in options, 'option[value=?]', "cf_#{i_cf.id}.cf_#{u_cf.id}", text: "User's Phone number" assert_select_in options, 'optgroup[label=?]', 'User', 1 end + + def test_filters_options_for_select_should_group_author_filters + with_locale 'en' do + options = filters_options_for_select(IssueQuery.new) + assert_select_in options, 'optgroup[label=?]', "Author", 1 + assert_select_in options, 'optgroup > option[value=author_group]', :text => "Author's group" + assert_select_in options, 'optgroup > option[value=author_role]', :text => "Author's role" + end + end end diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 462934f1e..23f6c7738 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -2788,6 +2788,28 @@ class QueryTest < ActiveSupport::TestCase assert ! query.available_filters["assigned_to_role"][:values].include?(['Anonymous', '5']) end + def test_available_filters_should_include_author_group_filter + query = IssueQuery.new + assert query.available_filters.key?("author_group") + assert_equal :list, query.available_filters["author_group"][:type] + assert query.available_filters["author_group"][:values].present? + assert_equal Group.givable.sort.map {|g| [g.name, g.id.to_s]}, + query.available_filters["author_group"][:values].sort + end + + def test_available_filters_should_include_author_role_filter + query = IssueQuery.new + assert query.available_filters.key?("author_role") + assert_equal :list, query.available_filters["author_role"][:type] + + assert query.available_filters["author_role"][:values].include?(['Manager', '1']) + assert query.available_filters["author_role"][:values].include?(['Developer', '2']) + assert query.available_filters["author_role"][:values].include?(['Reporter', '3']) + + assert ! query.available_filters["author_role"][:values].include?(['Non member', '4']) + assert ! query.available_filters["author_role"][:values].include?(['Anonymous', '5']) + end + def test_available_filters_should_include_custom_field_according_to_user_visibility visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true) hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1]) @@ -2958,6 +2980,44 @@ class QueryTest < ActiveSupport::TestCase assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query end + def test_author_group_filter_should_return_issues_with_or_without_author_in_group + project = Project.generate! + author = User.generate! + author_group = Group.generate! + not_author_group = Group.generate! + author.groups << author_group + issues = 3.times.collect { Issue.generate!(:project => project, :author => author) } + + query = IssueQuery.new(:name => '_', :project => project) + query.add_filter('author_group', '=', [author_group.id.to_s]) + assert_equal 3, query.issues.count + assert_query_result issues, query + + query = IssueQuery.new(:name => '_', :project => project) + query.add_filter('author_group', '!', [not_author_group.id.to_s]) + assert_equal 3, query.issues.count + assert_query_result issues, query + end + + def test_author_role_filter_should_return_issues_with_or_without_author_in_role + project = Project.generate! + author = User.generate! + manager_role = Role.find_by_name('Manager') + developer_role = Role.find_by_name('Developer') + User.add_to_project(author, project, manager_role) + issues = 3.times.collect { Issue.generate!(:project => project, :author => author) } + + query = IssueQuery.new(:name => 'issues generated by manager', :project => project) + query.add_filter('author_role', '=', [manager_role.id.to_s]) + assert_equal 3, query.issues.count + assert_query_result issues, query + + query = IssueQuery.new(:name => 'issues does not generated by developer', :project => project) + query.add_filter('author_role', '!', [developer_role.id.to_s]) + assert_equal 3, query.issues.count + assert_query_result issues, query + end + def test_query_column_should_accept_a_symbol_as_caption set_language_if_valid 'en' c = QueryColumn.new('foo', :caption => :general_text_Yes)