Feature #34040 » feature-34040-v2.patch
| app/helpers/queries_helper.rb | ||
|---|---|---|
| 318 | 318 | |
| 319 | 319 |
def query_to_csv(items, query, options={})
|
| 320 | 320 |
columns = query.columns |
| 321 |
journal_notes_columns = query.journal_notes_columns |
|
| 321 | 322 | |
| 322 | 323 |
Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv| |
| 323 | 324 |
# csv header fields |
| 324 | 325 |
csv << columns.map {|c| c.caption.to_s}
|
| 325 | 326 |
# csv lines |
| 326 |
items.each do |item| |
|
| 327 |
csv << columns.map {|c| csv_content(c, item)}
|
|
| 327 |
if journal_notes_columns.present? |
|
| 328 |
without_journal_notes_columns = query.without_journal_notes_columns |
|
| 329 | ||
| 330 |
items.each do |item| |
|
| 331 |
if item.journals_with_notes.blank? |
|
| 332 |
csv << without_journal_notes_columns.map {|c| csv_content(c, item)}
|
|
| 333 |
next |
|
| 334 |
end |
|
| 335 | ||
| 336 |
item.journals_with_notes.each_with_index do |journal, index| |
|
| 337 |
if index == 0 |
|
| 338 |
csv << without_journal_notes_columns.map {|c| csv_content(c, item)} + journal_notes_columns.map {|c| csv_content(c, journal)}
|
|
| 339 |
else |
|
| 340 |
csv << without_journal_notes_columns.map {|c| csv_content(c, item) if c.name == :id } + journal_notes_columns.map {|c| csv_content(c, journal)}
|
|
| 341 |
end |
|
| 342 |
end |
|
| 343 |
end |
|
| 344 |
else |
|
| 345 |
items.each do |item| |
|
| 346 |
csv << columns.map {|c| csv_content(c, item)}
|
|
| 347 |
end |
|
| 328 | 348 |
end |
| 329 | 349 |
end |
| 330 | 350 |
end |
| ... | ... | |
| 344 | 364 |
elsif api_request? || params[:set_filter] || !use_session || |
| 345 | 365 |
session[session_key].nil? || |
| 346 | 366 |
session[session_key][:project_id] != (@project ? @project.id : nil) |
| 367 |
# Replace journal_notes params |
|
| 368 |
if params[:journal_notes] && params[:format] == 'csv' |
|
| 369 |
params['c'] ||= [] |
|
| 370 |
params['c'] += IssueQuery.journal_column_names.map(&:to_s) |
|
| 371 |
end |
|
| 372 | ||
| 347 | 373 |
# Give it a name, required to be valid |
| 348 | 374 |
@query = klass.new(:name => "_", :project => @project) |
| 349 | 375 |
@query.build_from_params(params, options[:defaults]) |
| app/models/issue.rb | ||
|---|---|---|
| 263 | 263 |
@total_estimated_hours = nil |
| 264 | 264 |
@last_updated_by = nil |
| 265 | 265 |
@last_notes = nil |
| 266 |
@journals_with_notes = nil |
|
| 266 | 267 |
base_reload(*args) |
| 267 | 268 |
end |
| 268 | 269 | |
| ... | ... | |
| 1173 | 1174 |
def last_notes |
| 1174 | 1175 |
if @last_notes |
| 1175 | 1176 |
@last_notes |
| 1177 |
elsif @journals_with_notes && @journals_with_notes.first.notes |
|
| 1178 |
@journals_with_notes.first.notes |
|
| 1176 | 1179 |
else |
| 1177 | 1180 |
journals.visible.where.not(notes: '').reorder(:id => :desc).first.try(:notes) |
| 1178 | 1181 |
end |
| 1179 | 1182 |
end |
| 1180 | 1183 | |
| 1184 |
def journals_with_notes |
|
| 1185 |
if @journals_with_notes |
|
| 1186 |
@journals_with_notes |
|
| 1187 |
else |
|
| 1188 |
journals.where.not(notes: '').reorder(:id => :asc) |
|
| 1189 |
end |
|
| 1190 |
end |
|
| 1191 | ||
| 1181 | 1192 |
# Preloads relations for a collection of issues |
| 1182 | 1193 |
def self.load_relations(issues) |
| 1183 | 1194 |
if issues.any? |
| ... | ... | |
| 1268 | 1279 |
end |
| 1269 | 1280 | |
| 1270 | 1281 |
# Preloads visible last notes for a collection of issues |
| 1271 |
def self.load_visible_last_notes(issues, user=User.current) |
|
| 1282 |
def self.load_visible_last_notes(issues, user=User.current, has_notes=false)
|
|
| 1272 | 1283 |
if issues.any? |
| 1284 |
if has_notes |
|
| 1285 |
issues.each do |issue| |
|
| 1286 |
issue.instance_variable_set(:@last_notes, issue.journals_with_notes.last.notes) |
|
| 1287 |
end |
|
| 1288 |
return |
|
| 1289 |
end |
|
| 1290 | ||
| 1273 | 1291 |
issue_ids = issues.map(&:id) |
| 1274 | 1292 |
journal_ids = Journal.joins(issue: :project). |
| 1275 | 1293 |
where(:journalized_type => 'Issue', :journalized_id => issue_ids). |
| ... | ... | |
| 1287 | 1305 |
end |
| 1288 | 1306 |
end |
| 1289 | 1307 | |
| 1308 |
# Preloads visible journals_with_notes for a collection of issues |
|
| 1309 |
def self.load_visible_journals_with_notes(issues, user=User.current) |
|
| 1310 |
return unless issues.any? |
|
| 1311 | ||
| 1312 |
issue_ids = issues.map(&:id) |
|
| 1313 |
journals = Journal.joins(issue: :project). |
|
| 1314 |
where(:journalized_type => 'Issue', :journalized_id => issue_ids). |
|
| 1315 |
where(Journal.visible_notes_condition(user, :skip_pre_condition => true)). |
|
| 1316 |
where.not(notes: ''). |
|
| 1317 |
reorder(id: :asc) |
|
| 1318 | ||
| 1319 |
issues.each do |issue| |
|
| 1320 |
issue.instance_variable_set(:@journals_with_notes, journals.where(journalized_id: issue.id)) |
|
| 1321 |
end |
|
| 1322 |
end |
|
| 1323 | ||
| 1290 | 1324 |
# Finds an issue relation given its id. |
| 1291 | 1325 |
def find_relation(relation_id) |
| 1292 | 1326 |
IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id)
|
| app/models/issue_query.rb | ||
|---|---|---|
| 70 | 70 |
QueryColumn.new(:relations, :caption => :label_related_issues), |
| 71 | 71 |
QueryColumn.new(:attachments, :caption => :label_attachment_plural), |
| 72 | 72 |
QueryColumn.new(:description, :inline => false), |
| 73 |
QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false) |
|
| 73 |
QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false), |
|
| 74 |
QueryJournalsColumn.new(:id, :caption => :label_notes_id), |
|
| 75 |
QueryJournalsColumn.new(:notes, :caption => :field_notes), |
|
| 76 |
QueryJournalsColumn.new(:user, :caption => :label_notes_author), |
|
| 77 |
QueryJournalsColumn.new(:created_on, :caption => :label_notes_created_on), |
|
| 78 |
QueryJournalsColumn.new(:private_notes, :caption => :field_private_notes) |
|
| 74 | 79 |
] |
| 75 | 80 | |
| 76 | 81 |
has_many :projects, foreign_key: 'default_issue_query_id', dependent: :nullify, inverse_of: 'default_issue_query' |
| ... | ... | |
| 332 | 337 |
@available_columns |
| 333 | 338 |
end |
| 334 | 339 | |
| 340 |
def self.journal_column_names |
|
| 341 |
available_columns.select{|c| c.is_a?(QueryJournalsColumn)}.map(&:name)
|
|
| 342 |
end |
|
| 343 | ||
| 335 | 344 |
def default_columns_names |
| 336 | 345 |
@default_columns_names ||= begin |
| 337 | 346 |
default_columns = Setting.issue_list_default_columns.map(&:to_sym) |
| ... | ... | |
| 414 | 423 |
if has_column?(:relations) |
| 415 | 424 |
Issue.load_visible_relations(issues) |
| 416 | 425 |
end |
| 426 |
if IssueQuery.journal_column_names.any?{|name| has_column?(name)}
|
|
| 427 |
Issue.load_visible_journals_with_notes(issues) |
|
| 428 |
end |
|
| 417 | 429 |
if has_column?(:last_notes) |
| 418 |
Issue.load_visible_last_notes(issues) |
|
| 430 |
Issue.load_visible_last_notes(issues, User.current, IssueQuery.journal_column_names.any?{|name| has_column?(name)})
|
|
| 419 | 431 |
end |
| 420 | 432 |
issues |
| 421 | 433 |
rescue ::ActiveRecord::StatementInvalid => e |
| app/models/query.rb | ||
|---|---|---|
| 62 | 62 |
@inline |
| 63 | 63 |
end |
| 64 | 64 | |
| 65 |
def journal_notes? |
|
| 66 |
false |
|
| 67 |
end |
|
| 68 | ||
| 65 | 69 |
def frozen? |
| 66 | 70 |
@frozen |
| 67 | 71 |
end |
| ... | ... | |
| 201 | 205 |
end |
| 202 | 206 |
end |
| 203 | 207 | |
| 208 |
class QueryJournalsColumn < QueryColumn |
|
| 209 |
def initialize(name, options={})
|
|
| 210 |
@attribute = name |
|
| 211 |
@inline = false |
|
| 212 |
name_with_assoc = "journals.#{name}".to_sym
|
|
| 213 |
super(name_with_assoc, options) |
|
| 214 |
end |
|
| 215 | ||
| 216 |
def value_object(object) |
|
| 217 |
return nil unless object.is_a?(Journal) |
|
| 218 | ||
| 219 |
object.send @attribute |
|
| 220 |
end |
|
| 221 | ||
| 222 |
def journal_notes? |
|
| 223 |
true |
|
| 224 |
end |
|
| 225 |
end |
|
| 226 | ||
| 204 | 227 |
class QueryFilter |
| 205 | 228 |
include Redmine::I18n |
| 206 | 229 | |
| ... | ... | |
| 800 | 823 |
columns.reject(&:inline?) |
| 801 | 824 |
end |
| 802 | 825 | |
| 826 |
def journal_notes_columns |
|
| 827 |
columns.select(&:journal_notes?) |
|
| 828 |
end |
|
| 829 | ||
| 830 |
def without_journal_notes_columns |
|
| 831 |
columns.reject(&:journal_notes?) |
|
| 832 |
end |
|
| 833 | ||
| 803 | 834 |
def available_inline_columns |
| 804 | 835 |
available_columns.select(&:inline?) |
| 805 | 836 |
end |
| 806 | 837 | |
| 807 | 838 |
def available_block_columns |
| 808 |
available_columns.reject(&:inline?) |
|
| 839 |
available_columns.reject(&:inline?).reject(&:journal_notes?)
|
|
| 809 | 840 |
end |
| 810 | 841 | |
| 811 | 842 |
def available_totalable_columns |
| 812 | 843 |
available_columns.select(&:totalable) |
| 813 | 844 |
end |
| 814 | 845 | |
| 846 |
def available_journal_notes_columns |
|
| 847 |
available_columns.select(&:journal_notes?) |
|
| 848 |
end |
|
| 849 | ||
| 815 | 850 |
def default_columns_names |
| 816 | 851 |
[] |
| 817 | 852 |
end |
| app/views/issues/index.html.erb | ||
|---|---|---|
| 47 | 47 |
<label><%= radio_button_tag 'c[]', '', true %> <%= l(:description_selected_columns) %></label><br /> |
| 48 | 48 |
<label><%= radio_button_tag 'c[]', 'all_inline' %> <%= l(:description_all_columns) %></label> |
| 49 | 49 |
</p> |
| 50 |
<% if @query.available_block_columns.any? %> |
|
| 51 |
<fieldset id="csv-export-block-columns">
|
|
| 50 |
<% if @query.available_block_columns.any? || @query.available_journal_notes_columns.any? %>
|
|
| 51 |
<fieldset id="csv-export-other-columns">
|
|
| 52 | 52 |
<legend> |
| 53 |
<%= toggle_checkboxes_link('#csv-export-block-columns input[type=checkbox]') %>
|
|
| 53 |
<%= toggle_checkboxes_link('#csv-export-other-columns input[type=checkbox]') %>
|
|
| 54 | 54 |
</legend> |
| 55 |
<% @query.available_block_columns.each do |column| %> |
|
| 56 |
<label><%= check_box_tag 'c[]', column.name, @query.has_column?(column), :id => nil %> <%= column.caption %></label> |
|
| 55 |
<% if @query.available_block_columns.any? %> |
|
| 56 |
<% @query.available_block_columns.each do |column| %> |
|
| 57 |
<label><%= check_box_tag 'c[]', column.name, @query.has_column?(column), :id => nil %> <%= column.caption %></label> |
|
| 58 |
<% end %> |
|
| 59 |
<% end %> |
|
| 60 |
<% if @query.available_journal_notes_columns.any? %> |
|
| 61 |
<label><%= check_box_tag 'journal_notes', 1, params[:journal_notes], :id => nil %> <%= l(:label_all_notes) %></label> |
|
| 57 | 62 |
<% end %> |
| 58 | 63 |
</fieldset> |
| 59 | 64 |
<% end %> |
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 960 | 960 |
assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
|
| 961 | 961 |
end |
| 962 | 962 | |
| 963 |
def test_index_csv_with_journal_notes_params |
|
| 964 |
get( |
|
| 965 |
:index, |
|
| 966 |
:params => {
|
|
| 967 |
:format => 'csv', |
|
| 968 |
:c => ['subject'], |
|
| 969 |
:project => 'ecookbook', |
|
| 970 |
:journal_notes => 1 |
|
| 971 |
} |
|
| 972 |
) |
|
| 973 |
assert_response :success |
|
| 974 |
lines = @response.body.chomp.split("\n")
|
|
| 975 |
assert_equal 7, lines.first.split(',').count
|
|
| 976 | ||
| 977 |
journal1 = Journal.find(1) |
|
| 978 |
assert_equal "1,Cannot print recipes,#{journal1.id},#{journal1.notes},#{journal1.user},#{format_time(journal1.created_on)},No", lines[-2]
|
|
| 979 |
journal2 = Journal.find(2) |
|
| 980 |
assert_equal "1,,#{journal2.id},\"#{journal2.notes}\",#{journal2.user},#{format_time(journal2.created_on)},No", lines[-1]
|
|
| 981 |
end |
|
| 982 | ||
| 963 | 983 |
def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator |
| 964 | 984 |
field = |
| 965 | 985 |
IssueCustomField. |
| test/unit/query_test.rb | ||
|---|---|---|
| 1755 | 1755 |
assert_not_nil issues.first.instance_variable_get(:@last_notes) |
| 1756 | 1756 |
end |
| 1757 | 1757 | |
| 1758 |
def test_query_should_preload_journals_with_notes |
|
| 1759 |
q = IssueQuery.new(:name => '_', :column_names => [:subject, :'journals.notes']) |
|
| 1760 |
assert q.has_column?(:'journals.notes') |
|
| 1761 |
issues = q.issues |
|
| 1762 |
assert_not_nil issues.first.instance_variable_get(:@journals_with_notes) |
|
| 1763 |
end |
|
| 1764 | ||
| 1758 | 1765 |
def test_groupable_columns_should_include_custom_fields |
| 1759 | 1766 |
q = IssueQuery.new |
| 1760 | 1767 |
column = q.groupable_columns.detect {|c| c.name == :cf_1}
|
- « Previous
- 1
- …
- 3
- 4
- 5
- Next »