diff --git a/app/views/queries/_form.html.erb b/app/views/queries/_form.html.erb index 6a94712e01..00b65ed157 100644 --- a/app/views/queries/_form.html.erb +++ b/app/views/queries/_form.html.erb @@ -83,7 +83,16 @@ <%= label_tag "query_sort_criteria_direction_" + i.to_s, l(:description_query_sort_criteria_direction), :class => "hidden-for-sighted" %> <%= select_tag("query[sort_criteria][#{i}][]", - options_for_select([[], [l(:label_ascending), 'asc'], [l(:label_descending), 'desc']], @query.sort_criteria_order(i)), + options_for_select( + [ + [], + [l(:label_ascending), 'asc'], + [l(:label_descending), 'desc'], + [l(:label_ascending_null_last), 'asc nulls last'], + [l(:label_descending_null_first), 'desc nulls first'] + ], + @query.sort_criteria_order(i) + ), :id => "query_sort_criteria_direction_" + i.to_s) %>
<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 761e4194ca..4bc648f89d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1376,3 +1376,5 @@ en: twofa_text_group_disabled: "This setting is only effective when the global two factor authentication setting is set to 'optional'. Currently, two factor authentication is disabled." text_user_destroy_confirmation: "Are you sure you want to delete this user and remove all references to them? This cannot be undone. Often, locking a user instead of deleting them is the better solution. To confirm, please enter their login (%{login}) below." text_project_destroy_enter_identifier: "To confirm, please enter the project's identifier (%{identifier}) below." + label_ascending_null_last: Ascending nulls last + label_descending_null_first: Descending nulls first diff --git a/config/locales/ru.yml b/config/locales/ru.yml index ed89f3a6a7..d049d35994 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1513,3 +1513,5 @@ ru: label_subtask: Subtask label_default_query: Default query field_default_project_query: Default project query + label_ascending_null_last: По возрастанию с пустыми значениями в конце + label_descending_null_first: По убыванию с пустыми значениями в начале diff --git a/lib/redmine/sort_criteria.rb b/lib/redmine/sort_criteria.rb index 8ef1ba7b5d..d2433d7252 100644 --- a/lib/redmine/sort_criteria.rb +++ b/lib/redmine/sort_criteria.rb @@ -34,7 +34,16 @@ module Redmine end def to_param - self.collect {|k,o| k + (o == 'desc' ? ':desc' : '')}.join(',') + collect do |k, o| + k + (case o + when 'desc' + ':desc' + when /nulls (first|last)/ + ":#{o}" + else + '' + end) + end.join(',') end def to_a @@ -89,10 +98,20 @@ module Redmine private def normalize! - self.reject! {|s| s.first.blank?} - self.uniq! {|s| s.first} - self.collect! {|s| s = Array(s); [s.first, (s.last == false || s.last.to_s == 'desc') ? 'desc' : 'asc']} - self.replace self.first(3) + reject! {|s| s.first.blank? } + uniq! {|s| s.first } + collect! do |s| + s = Array(s) + [s.first, + if s.last == false || s.last.to_s == 'desc' # if sort is empty or desc => desc + 'desc' + elsif !s.last.blank? && s.size > 1 && /nulls (first|last)/.match?(s.last) # nulls sort + s.last + else + 'asc' + end] + end + replace first(3) end # Appends ASC/DESC to the sort criterion unless it has a fixed order diff --git a/test/helpers/sort_helper_test.rb b/test/helpers/sort_helper_test.rb index 163e48d6f9..363a111d02 100644 --- a/test/helpers/sort_helper_test.rb +++ b/test/helpers/sort_helper_test.rb @@ -102,6 +102,23 @@ class SortHelperTest < Redmine::HelperTest assert_equal 'sort-by-foo-bar sort-asc', sort_css_classes end + def test_nulls_sort + sort_init 'attr1', 'desc nulls first' + sort_update(['attr1', 'attr2']) + + assert_equal ['attr1 DESC NULLS FIRST'], sort_clause + end + + def test_params_nulls_sort + @sort_param = 'attr1,attr2:desc nulls last' + + sort_init 'attr1', 'desc' + sort_update({'attr1' => 'table1.attr1', 'attr2' => 'table2.attr2'}) + + assert_equal ['table1.attr1 ASC', 'table2.attr2 DESC NULLS LAST'], sort_clause + assert_equal 'attr1,attr2:desc nulls last', @session['foo_bar_sort'] + end + private def controller_name; 'foo'; end