diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index d55cc14aa..a37d75b2d 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -69,4 +69,50 @@ module SearchHelper links.map {|link| content_tag('li', link)}.join(' ').html_safe + ''.html_safe) unless links.empty? end + + def issues_filter_path(question, options) + projects_scope = options[:projects_scope] + titles_only = options[:titles_only] + all_words = options[:all_words] + open_issues = options[:open_issues] + + field_to_search = titles_only ? 'subject' : 'any_searchable' + params = { + :set_filter => 1, + :f => ['status_id', field_to_search], + :op => { + 'status_id' => open_issues ? 'o' : '*', + field_to_search => all_words ? '~' : '*~' + }, + :v => {field_to_search => [question]}, + :sort => 'updated_on:desc' + } + + case projects_scope + when 'all' + # nothing to do + when 'my_projects' + params[:f] << 'project_id' + params[:op]['project_id'] = '=' + params[:v]['project_id'] = ['mine'] + when 'bookmarks' + params[:f] << 'project_id' + params[:op]['project_id'] = '=' + params[:v]['project_id'] = ['bookmarks'] + when 'subprojects' + params[:f] << 'subproject_id' + params[:op]['subproject_id'] = '*' + params[:project_id] = @project.id + else + if @project + # current project only + params[:f] << 'subproject_id' + params[:op]['subproject_id'] = '!*' + params[:project_id] = @project.id + end + # else all projects + end + + issues_path(params) + end end diff --git a/app/views/search/index.html.erb b/app/views/search/index.html.erb index caaa3f35c..5902051ba 100644 --- a/app/views/search/index.html.erb +++ b/app/views/search/index.html.erb @@ -46,6 +46,11 @@ <%= render_results_by_type(@result_count_by_type) unless @scope.size == 1 %>

<%= l(:label_result_plural) %> (<%= @result_count %>)

+ <% if @result_count_by_type['issues'].to_i > 0 && @search_attachments == '0' %> +

+ <%= link_to l(:button_apply_issues_filter), issues_filter_path(@question, projects_scope: params[:scope], all_words: @all_words, titles_only: @titles_only, open_issues: @open_issues), :class => 'icon icon-list' %> +

+ <% end %>
<% @results.each do |e| %>
diff --git a/config/locales/en.yml b/config/locales/en.yml index 6b7d0834a..750bd1056 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1199,6 +1199,7 @@ en: button_edit_object: "Edit %{object_name}" button_delete_object: "Delete %{object_name}" button_create_and_follow: Create and follow + button_apply_issues_filter: Apply issues filter status_active: active status_registered: registered diff --git a/test/functional/search_controller_test.rb b/test/functional/search_controller_test.rb index ee5db1232..f4cfe7848 100644 --- a/test/functional/search_controller_test.rb +++ b/test/functional/search_controller_test.rb @@ -94,6 +94,7 @@ class SearchControllerTest < Redmine::ControllerTest assert_select 'input[name=all_words][checked=checked]' assert_select 'input[name=titles_only]:not([checked])' + assert_select 'p.buttons a', :text => 'Apply issues filter' assert_select '#search-results' do assert_select 'dt.issue a', :text => /Bug #5/ assert_select 'dt.issue-closed a', :text => /Bug #8 \(Closed\)/ @@ -456,4 +457,15 @@ class SearchControllerTest < Redmine::ControllerTest assert_select '#search-results dt.project', 0 end + + def test_search_should_not_show_apply_issues_filter_button_if_no_issues_found + get :index, :params => {:q => 'commits'} + assert_response :success + + assert_select 'p.buttons a', :text => 'Apply issues filter', :count => 0 + assert_select '#search-results' do + assert_select 'dt.issue', :count => 0 + assert_select 'dt.issue-closed', :count => 0 + end + end end diff --git a/test/helpers/search_helper_test.rb b/test/helpers/search_helper_test.rb index 33fe56a7b..12b8fa82f 100644 --- a/test/helpers/search_helper_test.rb +++ b/test/helpers/search_helper_test.rb @@ -54,4 +54,36 @@ class SearchHelperTest < Redmine::HelperTest r ) end + + def test_issues_filter_path + # rubocop:disable Layout/LineLength + assert_equal( + '/issues?f[]=status_id&f[]=any_searchable&f[]=project_id&op[any_searchable]=*~&op[project_id]==&op[status_id]=*&set_filter=1&sort=updated_on:desc&v[any_searchable][]=recipe&v[project_id][]=mine', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'my_projects')) + ) + assert_equal( + '/issues?f[]=status_id&f[]=any_searchable&f[]=project_id&op[any_searchable]=*~&op[project_id]==&op[status_id]=*&set_filter=1&sort=updated_on:desc&v[any_searchable][]=recipe&v[project_id][]=bookmarks', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'bookmarks')) + ) + assert_equal( + '/issues?f[]=status_id&f[]=any_searchable&op[any_searchable]=*~&op[status_id]=*&set_filter=1&sort=updated_on:desc&v[any_searchable][]=recipe', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'all')) + ) + # f[]=subject + assert_equal( + '/issues?f[]=status_id&f[]=subject&op[status_id]=*&op[subject]=*~&set_filter=1&sort=updated_on:desc&v[subject][]=recipe', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'all', titles_only: '1')) + ) + # op[subject]=~ (contains) + assert_equal( + '/issues?f[]=status_id&f[]=subject&op[status_id]=*&op[subject]=~&set_filter=1&sort=updated_on:desc&v[subject][]=recipe', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'all', titles_only: '1', all_words: '')) + ) + # op[status_id]=o (open) + assert_equal( + '/issues?f[]=status_id&f[]=subject&op[status_id]=o&op[subject]=*~&set_filter=1&sort=updated_on:desc&v[subject][]=recipe', + Addressable::URI.unencode(issues_filter_path('recipe', projects_scope: 'all', titles_only: '1', open_issues: '1')) + ) + # rubocop:enable Layout/LineLength + end end