export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
- csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
+ csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
# csv lines
issues.each do |issue|
- csv << [ issue.id.to_s ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, issue), encoding) }
+ csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, issue), encoding) }
end
end
export
when 'Date'
format_date(value)
when 'Fixnum'
- if column.name == :done_ratio
+ if column.name == :id
+ link_to value, issue_path(issue)
+ elsif column.name == :done_ratio
progress_bar(value, :width => '80px')
else
value.to_s
self.queried_class = Issue
self.available_columns = [
+ QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true),
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
@available_columns
end
- def sortable_columns
- {'id' => "#{Issue.table_name}.id"}.merge(super)
- end
-
def default_columns_names
@default_columns_names ||= begin
default_columns = Setting.issue_list_default_columns.map(&:to_sym)
end
self.default_order = options[:default_order]
@inline = options.key?(:inline) ? options[:inline] : true
- @caption_key = options[:caption] || "field_#{name}"
+ @caption_key = options[:caption] || "field_#{name}".to_sym
+ @frozen = options[:frozen]
end
def caption
- l(@caption_key)
+ @caption_key.is_a?(Symbol) ? l(@caption_key) : @caption_key
end
# Returns true if the column is sortable, otherwise false
@inline
end
+ def frozen?
+ @frozen
+ end
+
def value(object)
object.send name
end
def columns
# preserve the column_names order
- (has_default_columns? ? default_columns_names : column_names).collect do |name|
+ cols = (has_default_columns? ? default_columns_names : column_names).collect do |name|
available_columns.find { |col| col.name == name }
end.compact
+ available_columns.select(&:frozen?) | cols
end
def inline_columns
:onclick => 'toggleIssuesSelection(this); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
- <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
<% query.inline_columns.each do |column| %>
<%= column_header(column) %>
<% end %>
<% end %>
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
- <td class="id"><%= link_to issue.id, issue_path(issue) %></td>
<%= raw query.inline_columns.map {|column| "<td class=\"#{column.css_classes}\">#{column_content(column, issue)}</td>"}.join %>
</tr>
<% @query.block_columns.each do |column|
if (text = column_content(column, issue)) && text.present? -%>
<tr class="<%= current_cycle %>">
- <td colspan="<%= @query.inline_columns.size + 2 %>" class="<%= column.css_classes %>"><%= text %></td>
+ <td colspan="<%= @query.inline_columns.size + 1 %>" class="<%= column.css_classes %>"><%= text %></td>
</tr>
<% end -%>
<% end -%>
<%= label_tag "available_columns", l(:description_available_columns) %>
<br />
<%= select_tag 'available_columns',
- options_for_select((query.available_inline_columns - query.columns).collect {|column| [column.caption, column.name]}),
+ options_for_select((query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}),
:multiple => true, :size => 10, :style => "width:150px",
:ondblclick => "moveOptions(this.form.available_columns, this.form.selected_columns);" %>
</td>
<%= label_tag "selected_columns", l(:description_selected_columns) %>
<br />
<%= select_tag((defined?(tag_name) ? tag_name : 'c[]'),
- options_for_select(query.inline_columns.collect {|column| [column.caption, column.name]}),
+ options_for_select((query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}),
:id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px",
:ondblclick => "moveOptions(this.form.selected_columns, this.form.available_columns);") %>
</td>
assert_equal 'text/csv; header=present', @response.content_type
assert @response.body.starts_with?("#,")
lines = @response.body.chomp.split("\n")
- assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
+ assert_equal assigns(:query).columns.size, lines[0].split(',').size
end
def test_index_csv_with_project
assert_equal 'text/csv; header=present', @response.content_type
assert @response.body.starts_with?("#,")
lines = @response.body.chomp.split("\n")
- assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
+ assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
end
def test_index_csv_with_spent_time_column
assert_response :success
assert_not_nil assigns(:issues)
assert_equal 'text/csv; header=present', @response.content_type
- assert @response.body.starts_with?("#,")
- lines = @response.body.chomp.split("\n")
- assert_equal assigns(:query).available_inline_columns.size + 1, lines[0].split(',').size
+ assert_match /\A#,/, response.body
+ lines = response.body.chomp.split("\n")
+ assert_equal assigns(:query).available_inline_columns.size, lines[0].split(',').size
end
def test_index_csv_with_multi_column_field
# query should use specified columns
query = assigns(:query)
assert_kind_of IssueQuery, query
- assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
+ assert_equal [:id, :project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
end
def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
- columns = ['tracker', 'subject', 'assigned_to']
+ columns = ['id', 'tracker', 'subject', 'assigned_to']
get :index, :set_filter => 1, :c => columns
# query should use specified columns
assert_redirected_to :controller => 'issues', :action => 'index', :project_id => nil, :query_id => q
assert !q.is_public?
assert !q.has_default_columns?
- assert_equal [:tracker, :subject, :priority, :category], q.columns.collect {|c| c.name}
+ assert_equal [:id, :tracker, :subject, :priority, :category], q.columns.collect {|c| c.name}
assert q.valid?
end
def test_set_column_names
q = IssueQuery.new
q.column_names = ['tracker', :subject, '', 'unknonw_column']
- assert_equal [:tracker, :subject], q.columns.collect {|c| c.name}
- c = q.columns.first
- assert q.has_column?(c)
+ assert_equal [:id, :tracker, :subject], q.columns.collect {|c| c.name}
+ end
+
+ def test_has_column_should_accept_a_column_name
+ q = IssueQuery.new
+ q.column_names = ['tracker', :subject]
+ assert q.has_column?(:tracker)
+ assert !q.has_column?(:category)
+ end
+
+ def test_has_column_should_accept_a_column
+ q = IssueQuery.new
+ q.column_names = ['tracker', :subject]
+
+ tracker_column = q.available_columns.detect {|c| c.name==:tracker}
+ assert_kind_of QueryColumn, tracker_column
+ category_column = q.available_columns.detect {|c| c.name==:category}
+ assert_kind_of QueryColumn, category_column
+
+ assert q.has_column?(tracker_column)
+ assert !q.has_column?(category_column)
end
def test_inline_and_block_columns
q = IssueQuery.new
q.column_names = ['subject', 'description', 'tracker']
- assert_equal [:subject, :tracker], q.inline_columns.map(&:name)
+ assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)
assert_equal [:description], q.block_columns.map(&:name)
end