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?
@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
normalize!
end
+ def criteria=(arg)
+ @criteria = arg
+ normalize!
+ end
+
def to_param
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
end
@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).
- #
- # - default_key is a column attribute name.
- # - default_order is 'asc' or 'desc'.
+ # Initializes the default sort.
+ # Examples:
+ #
+ # sort_init 'name'
+ # sort_init 'id', 'desc'
+ # sort_init ['name', ['id', 'desc']]
+ # sort_init [['name', 'desc'], ['id', '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
# - 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.
#
# <%= 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'
def caption
l("field_#{name}")
end
+
+ # Returns true if the column is sortable, otherwise false
+ def sortable?
+ !sortable.nil?
+ end
end
class QueryCustomFieldColumn < QueryColumn
belongs_to :user
serialize :filters
serialize :column_names
+ serialize :sort_criteria, Array
attr_protected :project_id, :user_id
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?
<%= 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>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
enumeration_doc_categories: Dokumentkategorier
enumeration_activities: Aktiviteter (tidsuppföljning)
field_content: Content
+ label_descending: Descending
+ label_sort: Sort
+ label_ascending: Ascending
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
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
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
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
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
field_content: Content
+ label_descending: Descending
+ label_sort: Sort
+ label_ascending: Ascending
enumeration_doc_categories: 文档类别
enumeration_activities: 活动(时间跟踪)
field_content: Content
+ label_descending: Descending
+ label_sort: Sort
+ label_ascending: Ascending
--- /dev/null
+class AddQueriesSortCriteria < ActiveRecord::Migration
+ def self.up
+ add_column :queries, :sort_criteria, :text
+ end
+
+ def self.down
+ remove_column :queries, :sort_criteria
+ end
+end
\r
user_id: 2\r
column_names: \r
+queries_005: \r
+ id: 5\r
+ project_id: \r
+ is_public: true\r
+ name: Open issues by priority and tracker\r
+ filters: |\r
+ status_id: \r
+ :values: \r
+ - "1"\r
+ :operator: o\r
+\r
+ user_id: 1\r
+ column_names: \r
+ sort_criteria: |\r
+ --- \r
+ - - priority\r
+ - desc\r
+ - - tracker\r
+ - asc\r
+
\ No newline at end of file
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
: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
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' }