Ability to save "sort order" in custom queries (#2899).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2572 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
2b585407cb
commit
c7c8dc71f2
|
@ -45,7 +45,7 @@ class IssuesController < ApplicationController
|
|||
|
||||
def index
|
||||
retrieve_query
|
||||
sort_init 'id', 'desc'
|
||||
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
|
||||
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
|
||||
|
||||
if @query.valid?
|
||||
|
@ -471,6 +471,7 @@ private
|
|||
@query = Query.find(params[:query_id], :conditions => cond)
|
||||
@query.project = @project
|
||||
session[:query] = {:id => @query.id, :project_id => @query.project_id}
|
||||
sort_clear
|
||||
else
|
||||
if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
|
||||
# Give it a name, required to be valid
|
||||
|
|
|
@ -69,6 +69,11 @@ module SortHelper
|
|||
normalize!
|
||||
end
|
||||
|
||||
def criteria=(arg)
|
||||
@criteria = arg
|
||||
normalize!
|
||||
end
|
||||
|
||||
def to_param
|
||||
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
|
||||
end
|
||||
|
@ -102,24 +107,42 @@ module SortHelper
|
|||
@criteria.first && @criteria.first.last
|
||||
end
|
||||
|
||||
def empty?
|
||||
@criteria.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize!
|
||||
@criteria = @criteria.collect {|s| [s.first, (s.last == false || s.last == 'desc') ? false : true]}
|
||||
@criteria ||= []
|
||||
@criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
|
||||
@criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
|
||||
@criteria.slice!(3)
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def sort_name
|
||||
controller_name + '_' + action_name + '_sort'
|
||||
end
|
||||
|
||||
# Initializes the default sort column (default_key) and sort order
|
||||
# (default_order).
|
||||
# Initializes the default sort.
|
||||
# Examples:
|
||||
#
|
||||
# sort_init 'name'
|
||||
# sort_init 'id', 'desc'
|
||||
# sort_init ['name', ['id', 'desc']]
|
||||
# sort_init [['name', 'desc'], ['id', 'desc']]
|
||||
#
|
||||
# - default_key is a column attribute name.
|
||||
# - default_order is 'asc' or 'desc'.
|
||||
#
|
||||
def sort_init(default_key, default_order='asc')
|
||||
@sort_default = "#{default_key}:#{default_order}"
|
||||
def sort_init(*args)
|
||||
case args.size
|
||||
when 1
|
||||
@sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
|
||||
when 2
|
||||
@sort_default = [[args.first, args.last]]
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
# Updates the sort state. Call this in the controller prior to calling
|
||||
|
@ -127,13 +150,18 @@ module SortHelper
|
|||
# - criteria can be either an array or a hash of allowed keys
|
||||
#
|
||||
def sort_update(criteria)
|
||||
sort_name = controller_name + '_' + action_name + '_sort'
|
||||
|
||||
@sort_criteria = SortCriteria.new
|
||||
@sort_criteria.available_criteria = criteria
|
||||
@sort_criteria.from_param(params[:sort] || session[sort_name] || @sort_default)
|
||||
@sort_criteria.from_param(params[:sort] || session[sort_name])
|
||||
@sort_criteria.criteria = @sort_default if @sort_criteria.empty?
|
||||
session[sort_name] = @sort_criteria.to_param
|
||||
end
|
||||
|
||||
# Clears the sort criteria session data
|
||||
#
|
||||
def sort_clear
|
||||
session[sort_name] = nil
|
||||
end
|
||||
|
||||
# Returns an SQL sort clause corresponding to the current sort state.
|
||||
# Use this to sort the controller's table items collection.
|
||||
|
@ -188,13 +216,6 @@ module SortHelper
|
|||
#
|
||||
# <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
|
||||
#
|
||||
# Renders:
|
||||
#
|
||||
# <th title="Sort by contact ID" width="40">
|
||||
# <a href="/contact/list?sort_order=desc&sort_key=id">Id</a>
|
||||
# <img alt="Sort_asc" src="/images/sort_asc.png" />
|
||||
# </th>
|
||||
#
|
||||
def sort_header_tag(column, options = {})
|
||||
caption = options.delete(:caption) || column.to_s.humanize
|
||||
default_order = options.delete(:default_order) || 'asc'
|
||||
|
|
|
@ -28,6 +28,11 @@ class QueryColumn
|
|||
def caption
|
||||
l("field_#{name}")
|
||||
end
|
||||
|
||||
# Returns true if the column is sortable, otherwise false
|
||||
def sortable?
|
||||
!sortable.nil?
|
||||
end
|
||||
end
|
||||
|
||||
class QueryCustomFieldColumn < QueryColumn
|
||||
|
@ -52,6 +57,7 @@ class Query < ActiveRecord::Base
|
|||
belongs_to :user
|
||||
serialize :filters
|
||||
serialize :column_names
|
||||
serialize :sort_criteria, Array
|
||||
|
||||
attr_protected :project_id, :user_id
|
||||
|
||||
|
@ -261,6 +267,27 @@ class Query < ActiveRecord::Base
|
|||
column_names.nil? || column_names.empty?
|
||||
end
|
||||
|
||||
def sort_criteria=(arg)
|
||||
c = []
|
||||
if arg.is_a?(Hash)
|
||||
arg = arg.keys.sort.collect {|k| arg[k]}
|
||||
end
|
||||
c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
|
||||
write_attribute(:sort_criteria, c)
|
||||
end
|
||||
|
||||
def sort_criteria
|
||||
read_attribute(:sort_criteria) || []
|
||||
end
|
||||
|
||||
def sort_criteria_key(arg)
|
||||
sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
|
||||
end
|
||||
|
||||
def sort_criteria_order(arg)
|
||||
sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
|
||||
end
|
||||
|
||||
def project_statement
|
||||
project_clauses = []
|
||||
if project && !@project.descendants.active.empty?
|
||||
|
|
|
@ -25,5 +25,14 @@
|
|||
<%= render :partial => 'queries/filters', :locals => {:query => query}%>
|
||||
</fieldset>
|
||||
|
||||
<fieldset><legend><%= l(:label_sort) %></legend>
|
||||
<% 3.times do |i| %>
|
||||
<%= i+1 %>: <%= select_tag("query[sort_criteria][#{i}][]",
|
||||
options_for_select([[]] + query.available_columns.select(&:sortable?).collect {|column| [column.caption, column.name.to_s]}, @query.sort_criteria_key(i))) %>
|
||||
<%= select_tag("query[sort_criteria][#{i}][]",
|
||||
options_for_select([[], [l(:label_ascending), 'asc'], [l(:label_descending), 'desc']], @query.sort_criteria_order(i))) %><br />
|
||||
<% end %>
|
||||
</fieldset>
|
||||
|
||||
<%= render :partial => 'queries/columns', :locals => {:query => query}%>
|
||||
</div>
|
||||
|
|
|
@ -778,3 +778,6 @@ bg:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -779,3 +779,6 @@ ca:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -783,3 +783,6 @@ cs:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -811,3 +811,6 @@ da:
|
|||
setting_per_page_options: Objects per page options
|
||||
mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -810,3 +810,6 @@ de:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -662,6 +662,9 @@ en:
|
|||
label_issue_watchers: Watchers
|
||||
label_example: Example
|
||||
label_display: Display
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
label_descending: Descending
|
||||
|
||||
button_login: Login
|
||||
button_submit: Submit
|
||||
|
|
|
@ -831,3 +831,6 @@ es:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -821,3 +821,6 @@ fi:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -694,6 +694,9 @@ fr:
|
|||
label_issue_watchers: Observateurs
|
||||
label_example: Exemple
|
||||
label_display: Affichage
|
||||
label_sort: Tri
|
||||
label_ascending: Croissant
|
||||
label_descending: Décroissant
|
||||
|
||||
button_login: Connexion
|
||||
button_submit: Soumettre
|
||||
|
|
|
@ -810,3 +810,6 @@ gl:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -793,3 +793,6 @@ he:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -816,3 +816,6 @@
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: bejelentkezés OpenID használatával
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -796,3 +796,6 @@ it:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -809,3 +809,6 @@ ja:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -840,3 +840,6 @@ ko:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -821,3 +821,6 @@ lt:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -766,3 +766,6 @@ nl:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -783,3 +783,6 @@
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -814,3 +814,6 @@ pl:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -816,3 +816,6 @@ pt-BR:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: ou use o OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -802,3 +802,6 @@ pt:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -823,3 +823,6 @@ ro:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -909,3 +909,6 @@ ru:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -782,3 +782,6 @@ sk:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -780,3 +780,6 @@ sl:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -804,3 +804,6 @@
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -838,3 +838,6 @@ sv:
|
|||
enumeration_doc_categories: Dokumentkategorier
|
||||
enumeration_activities: Aktiviteter (tidsuppföljning)
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -781,3 +781,6 @@ th:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -817,3 +817,6 @@ tr:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -780,3 +780,6 @@ uk:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -850,3 +850,6 @@ vi:
|
|||
field_identity_url: OpenID URL
|
||||
label_login_with_open_id_option: or login with OpenID
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -888,3 +888,6 @@
|
|||
enumeration_doc_categories: 文件分類
|
||||
enumeration_activities: 活動 (時間追蹤)
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -813,3 +813,6 @@ zh:
|
|||
enumeration_doc_categories: 文档类别
|
||||
enumeration_activities: 活动(时间跟踪)
|
||||
field_content: Content
|
||||
label_descending: Descending
|
||||
label_sort: Sort
|
||||
label_ascending: Ascending
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class AddQueriesSortCriteria < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :queries, :sort_criteria, :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :queries, :sort_criteria
|
||||
end
|
||||
end
|
|
@ -67,3 +67,23 @@ queries_004:
|
|||
|
||||
user_id: 2
|
||||
column_names:
|
||||
queries_005:
|
||||
id: 5
|
||||
project_id:
|
||||
is_public: true
|
||||
name: Open issues by priority and tracker
|
||||
filters: |
|
||||
status_id:
|
||||
:values:
|
||||
- "1"
|
||||
:operator: o
|
||||
|
||||
user_id: 1
|
||||
column_names:
|
||||
sort_criteria: |
|
||||
---
|
||||
- - priority
|
||||
- desc
|
||||
- - tracker
|
||||
- asc
|
||||
|
|
@ -111,6 +111,22 @@ class QueriesControllerTest < Test::Unit::TestCase
|
|||
assert q.valid?
|
||||
end
|
||||
|
||||
def test_new_with_sort
|
||||
@request.session[:user_id] = 1
|
||||
post :new,
|
||||
:confirm => '1',
|
||||
:default_columns => '1',
|
||||
:operators => {"status_id" => "o"},
|
||||
:values => {"status_id" => ["1"]},
|
||||
:query => {:name => "test_new_with_sort",
|
||||
:is_public => "1",
|
||||
:sort_criteria => {"0" => ["due_date", "desc"], "1" => ["tracker", ""]}}
|
||||
|
||||
query = Query.find_by_name("test_new_with_sort")
|
||||
assert_not_nil query
|
||||
assert_equal [['due_date', 'desc'], ['tracker', 'asc']], query.sort_criteria
|
||||
end
|
||||
|
||||
def test_get_edit_global_public_query
|
||||
@request.session[:user_id] = 1
|
||||
get :edit, :id => 4
|
||||
|
@ -202,6 +218,19 @@ class QueriesControllerTest < Test::Unit::TestCase
|
|||
:disabled => 'disabled' }
|
||||
end
|
||||
|
||||
def test_get_edit_sort_criteria
|
||||
@request.session[:user_id] = 1
|
||||
get :edit, :id => 5
|
||||
assert_response :success
|
||||
assert_template 'edit'
|
||||
assert_tag :tag => 'select', :attributes => { :name => 'query[sort_criteria][0][]' },
|
||||
:child => { :tag => 'option', :attributes => { :value => 'priority',
|
||||
:selected => 'selected' } }
|
||||
assert_tag :tag => 'select', :attributes => { :name => 'query[sort_criteria][0][]' },
|
||||
:child => { :tag => 'option', :attributes => { :value => 'desc',
|
||||
:selected => 'selected' } }
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
@request.session[:user_id] = 2
|
||||
post :destroy, :id => 1
|
||||
|
|
|
@ -195,6 +195,31 @@ class QueryTest < Test::Unit::TestCase
|
|||
assert q.has_column?(c)
|
||||
end
|
||||
|
||||
def test_default_sort
|
||||
q = Query.new
|
||||
assert_equal [], q.sort_criteria
|
||||
end
|
||||
|
||||
def test_set_sort_criteria_with_hash
|
||||
q = Query.new
|
||||
q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
|
||||
assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
|
||||
end
|
||||
|
||||
def test_set_sort_criteria_with_array
|
||||
q = Query.new
|
||||
q.sort_criteria = [['priority', 'desc'], 'tracker']
|
||||
assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
|
||||
end
|
||||
|
||||
def test_create_query_with_sort
|
||||
q = Query.new(:name => 'Sorted')
|
||||
q.sort_criteria = [['priority', 'desc'], 'tracker']
|
||||
assert q.save
|
||||
q.reload
|
||||
assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
|
||||
end
|
||||
|
||||
def test_sort_by_string_custom_field_asc
|
||||
q = Query.new
|
||||
c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
|
||||
|
|
Loading…
Reference in New Issue