Index: app/helpers/queries_helper.rb =================================================================== --- app/helpers/queries_helper.rb (revision 18582) +++ app/helpers/queries_helper.rb (working copy) @@ -41,6 +41,8 @@ group = :label_date elsif %w(estimated_hours spent_time).include?(field) group = :label_time_tracking + elsif field_options[:group] == 'or_filter' + group = :label_orfilter end if group (grouped[group] ||= []) << [field_options[:name], field] Index: app/models/issue_query.rb =================================================================== --- app/models/issue_query.rb (revision 18582) +++ app/models/issue_query.rb (working copy) @@ -202,6 +202,22 @@ add_available_filter "issue_id", :type => :integer, :label => :label_issue + add_available_filter "and_any", + :name => l(:label_orfilter_and_any), + :type => :list, + :values => [l(:general_text_Yes)], + :group => 'or_filter' + add_available_filter "or_any", + :name => l(:label_orfilter_or_any), + :type => :list, + :values => [l(:general_text_Yes)], + :group => 'or_filter' + add_available_filter "or_all", + :name => l(:label_orfilter_or_all), + :type => :list, + :values => [l(:general_text_Yes)], + :group => 'or_filter' + Tracker.disabled_core_fields(trackers).each {|field| delete_available_filter field } Index: app/models/query.rb =================================================================== --- app/models/query.rb (revision 18582) +++ app/models/query.rb (working copy) @@ -883,10 +883,37 @@ end def statement - # filters clauses - filters_clauses = [] + filters_clauses=[] + and_clauses=[] + and_any_clauses=[] + or_any_clauses=[] + or_all_clauses=[] + and_any_op = "" + or_any_op = "" + or_all_op = "" + + #the AND filter start first + filters_clauses = and_clauses + filters.each_key do |field| next if field == "subproject_id" + if field == "and_any" + #start the and any part, point filters_clause to and_any_clauses + filters_clauses = and_any_clauses + and_any_op = operator_for(field) == "=" ? " AND " : " AND NOT " + next + elsif field == "or_any" + #start the or any part, point filters_clause to or_any_clauses + filters_clauses = or_any_clauses + or_any_op = operator_for(field) == "=" ? " OR " : " OR NOT " + next + elsif field == "or_all" + #start the or any part, point filters_clause to or_any_clauses + filters_clauses = or_all_clauses + or_all_op = operator_for(field) == "=" ? " OR " : " OR NOT " + next + end + v = values_for(field).clone next unless v and !v.empty? operator = operator_for(field) @@ -916,7 +943,7 @@ filters_clauses << sql_for_custom_field(field, operator, v, $1) elsif field =~ /^cf_(\d+)\.(.+)$/ filters_clauses << sql_for_custom_field_attribute(field, operator, v, $1, $2) - elsif respond_to?(method = "sql_for_#{field.tr('.','_')}_field") + elsif respond_to?(method = "sql_for_#{field.gsub('.','_')}_field") # specific statement filters_clauses << send(method, field, operator, v) else @@ -930,10 +957,39 @@ filters_clauses << c.custom_field.visibility_by_project_condition end - filters_clauses << project_statement - filters_clauses.reject!(&:blank?) + #now start build the full statement, project filter is allways AND + and_clauses.reject!(&:blank?) + and_statement = and_clauses.any? ? and_clauses.join(" AND ") : nil - filters_clauses.any? ? filters_clauses.join(' AND ') : nil + all_and_statement = ["#{project_statement}", "#{and_statement}"].reject(&:blank?) + all_and_statement = all_and_statement.any? ? all_and_statement.join(" AND ") : nil + + + # finish the traditional part. Now extended part + # add the and_any first + and_any_clauses.reject!(&:blank?) + and_any_statement = and_any_clauses.any? ? "("+ and_any_clauses.join(" OR ") +")" : nil + + full_statement_ext_1 = ["#{all_and_statement}", "#{and_any_statement}"].reject(&:blank?) + full_statement_ext_1 = full_statement_ext_1.any? ? full_statement_ext_1.join(and_any_op) : nil + + # then add the or_all + or_all_clauses.reject!(&:blank?) + or_all_statement = or_all_clauses.any? ? "("+ or_all_clauses.join(" AND ") +")" : nil + + full_statement_ext_2 = ["#{full_statement_ext_1}", "#{or_all_statement}"].reject(&:blank?) + full_statement_ext_2 = full_statement_ext_2.any? ? full_statement_ext_2.join(or_all_op) : nil + + # then add the or_any + or_any_clauses.reject!(&:blank?) + or_any_statement = or_any_clauses.any? ? "("+ or_any_clauses.join(" OR ") +")" : nil + + full_statement = ["#{full_statement_ext_2}", "#{or_any_statement}"].reject(&:blank?) + full_statement = full_statement.any? ? full_statement.join(or_any_op) : nil + + Rails.logger.info "STATEMENT #{full_statement}" + + return full_statement end # Returns the result count by group or nil if query is not grouped Index: config/locales/de.yml =================================================================== --- config/locales/de.yml (revision 18582) +++ config/locales/de.yml (working copy) @@ -841,6 +841,10 @@ other: "%{count} Projekte" label_year: Jahr label_yesterday: gestern + label_orfilter: "ODER Filter" + label_orfilter_and_any: "UND einer der folgenden" + label_orfilter_or_any: "ODER einer der folgenden" + label_orfilter_or_all: "ODER alle folgenden" mail_body_account_activation_request: "Ein neuer Benutzer (%{value}) hat sich registriert. Sein Konto wartet auf Ihre Genehmigung:" mail_body_account_information: Ihre Konto-Informationen Index: config/locales/en.yml =================================================================== --- config/locales/en.yml (revision 18582) +++ config/locales/en.yml (working copy) @@ -1072,6 +1072,10 @@ label_password_char_class_lowercase: lowercase letters label_password_char_class_digits: digits label_password_char_class_special_chars: special characters + label_orfilter: "OR filters" + label_orfilter_and_any: "AND any following" + label_orfilter_or_any: "OR any following" + label_orfilter_or_all: "OR all following" button_login: Login button_submit: Submit Index: test/unit/query_test.rb =================================================================== --- test/unit/query_test.rb (revision 18582) +++ test/unit/query_test.rb (working copy) @@ -1381,6 +1381,66 @@ assert_equal [5, 8, 9], issues.collect(&:id).sort end + def test_filter_on_orfilter_and_any + query = IssueQuery.new(:name => '_') + query.filters = {'project_id' => {:operator => '=', :values => [1]}, + 'and_any' => {:operator => '=', :values => [1]}, + 'status_id' => {:operator => '!', :values => [1]}, + 'assigned_to_id' => {:operator => '=', :values => [3]}} + issues = find_issues_with_query(query) + assert_equal [2, 3, 8, 11, 12], issues.collect(&:id).sort + end + + def test_filter_on_orfilter_and_any_not + query = IssueQuery.new(:name => '_') + query.filters = {'project_id' => {:operator => '=', :values => [1]}, + 'and_any' => {:operator => '!', :values => [1]}, + 'status_id' => {:operator => '=', :values => [2]}, + 'author_id' => {:operator => '=', :values => [3]}} + issues = find_issues_with_query(query) + assert_equal [1, 3, 7, 8, 11], issues.collect(&:id).sort + end + + def test_filter_on_orfilter_or_any + query = IssueQuery.new(:name => '_') + query.filters = {'status_id' => {:operator => '!', :values => [1]}, + 'or_any' => {:operator => '=', :values => [1]}, + 'project_id' => {:operator => '=', :values => [3]}, + 'assigned_to_id' => {:operator => '=', :values => [2]}} + issues = find_issues_with_query(query) + assert_equal [2, 4, 5, 8, 11, 12, 13, 14], issues.collect(&:id).sort + end + + def test_filter_on_orfilter_or_any_not + query = IssueQuery.new(:name => '_') + query.filters = {'status_id' => {:operator => '!', :values => [1]}, + 'or_any' => {:operator => '!', :values => [1]}, + 'project_id' => {:operator => '=', :values => [3]}, + 'assigned_to_id' => {:operator => '!', :values => [2]}} + issues = find_issues_with_query(query) + assert_equal [2, 4, 8, 11, 12], issues.collect(&:id).sort + end + + def test_filter_on_orfilter_or_all + query = IssueQuery.new(:name => '_') + query.filters = {'project_id' => {:operator => '=', :values => [3]}, + 'or_all' => {:operator => '=', :values => [1]}, + 'author_id' => {:operator => '=', :values => [2]}, + 'assigned_to_id' => {:operator => '=', :values => [2]}} + issues = find_issues_with_query(query) + assert_equal [4, 5, 13, 14], issues.collect(&:id).sort + end + + def test_filter_on_orfilter_or_all_not + query = IssueQuery.new(:name => '_') + query.filters = {'project_id' => {:operator => '=', :values => [3]}, + 'or_all' => {:operator => '!', :values => [1]}, + 'author_id' => {:operator => '=', :values => [2]}, + 'assigned_to_id' => {:operator => '=', :values => [2]}} + issues = find_issues_with_query(query) + assert_equal [2, 3, 5, 12, 13, 14], issues.collect(&:id).sort + end + def test_statement_should_be_nil_with_no_filters q = IssueQuery.new(:name => '_') q.filters = {}