diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb
index 77a5779985..e4c4fd61e6 100644
--- a/app/helpers/queries_helper.rb
+++ b/app/helpers/queries_helper.rb
@@ -318,13 +318,33 @@ module QueriesHelper
def query_to_csv(items, query, options={})
columns = query.columns
+ journal_notes_columns = query.journal_notes_columns
Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv|
# csv header fields
csv << columns.map {|c| c.caption.to_s}
# csv lines
- items.each do |item|
- csv << columns.map {|c| csv_content(c, item)}
+ if journal_notes_columns.present?
+ without_journal_notes_columns = query.without_journal_notes_columns
+
+ items.each do |item|
+ if item.journals_with_notes.blank?
+ csv << without_journal_notes_columns.map {|c| csv_content(c, item)}
+ next
+ end
+
+ item.journals_with_notes.each_with_index do |journal, index|
+ if index == 0
+ csv << without_journal_notes_columns.map {|c| csv_content(c, item)} + journal_notes_columns.map {|c| csv_content(c, journal)}
+ else
+ csv << without_journal_notes_columns.map {|c| csv_content(c, item) if c.name == :id } + journal_notes_columns.map {|c| csv_content(c, journal)}
+ end
+ end
+ end
+ else
+ items.each do |item|
+ csv << columns.map {|c| csv_content(c, item)}
+ end
end
end
end
@@ -344,6 +364,12 @@ module QueriesHelper
elsif api_request? || params[:set_filter] || !use_session ||
session[session_key].nil? ||
session[session_key][:project_id] != (@project ? @project.id : nil)
+ # Replace journal_notes params
+ if params[:journal_notes] && params[:format] == 'csv'
+ params['c'] ||= []
+ params['c'] += IssueQuery.journal_column_names.map(&:to_s)
+ end
+
# Give it a name, required to be valid
@query = klass.new(:name => "_", :project => @project)
@query.build_from_params(params, options[:defaults])
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 02aaff33be..d1d88b1b89 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -261,6 +261,7 @@ class Issue < ActiveRecord::Base
@total_estimated_hours = nil
@last_updated_by = nil
@last_notes = nil
+ @journals_with_notes = nil
base_reload(*args)
end
@@ -1165,11 +1166,21 @@ class Issue < ActiveRecord::Base
def last_notes
if @last_notes
@last_notes
+ elsif @journals_with_notes && @journals_with_notes.first.notes
+ @journals_with_notes.first.notes
else
journals.where.not(notes: '').reorder(:id => :desc).first.try(:notes)
end
end
+ def journals_with_notes
+ if @journals_with_notes
+ @journals_with_notes
+ else
+ journals.where.not(notes: '').reorder(:id => :asc)
+ end
+ end
+
# Preloads relations for a collection of issues
def self.load_relations(issues)
if issues.any?
@@ -1260,8 +1271,15 @@ class Issue < ActiveRecord::Base
end
# Preloads visible last notes for a collection of issues
- def self.load_visible_last_notes(issues, user=User.current)
+ def self.load_visible_last_notes(issues, user=User.current, has_notes=false)
if issues.any?
+ if has_notes
+ issues.each do |issue|
+ issue.instance_variable_set(:@last_notes, issue.journals_with_notes.last.notes)
+ end
+ return
+ end
+
issue_ids = issues.map(&:id)
journal_ids = Journal.joins(issue: :project).
where(:journalized_type => 'Issue', :journalized_id => issue_ids).
@@ -1279,6 +1297,22 @@ class Issue < ActiveRecord::Base
end
end
+ # Preloads visible journals_with_notes for a collection of issues
+ def self.load_visible_journals_with_notes(issues, user=User.current)
+ return unless issues.any?
+
+ issue_ids = issues.map(&:id)
+ journals = Journal.joins(issue: :project).
+ where(:journalized_type => 'Issue', :journalized_id => issue_ids).
+ where(Journal.visible_notes_condition(user, :skip_pre_condition => true)).
+ where.not(notes: '').
+ reorder(id: :asc)
+
+ issues.each do |issue|
+ issue.instance_variable_set(:@journals_with_notes, journals.where(journalized_id: issue.id))
+ end
+ end
+
# Finds an issue relation given its id.
def find_relation(relation_id)
IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id)
diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb
index 5a3e14474d..ae54ee953d 100644
--- a/app/models/issue_query.rb
+++ b/app/models/issue_query.rb
@@ -70,7 +70,12 @@ class IssueQuery < Query
QueryColumn.new(:relations, :caption => :label_related_issues),
QueryColumn.new(:attachments, :caption => :label_attachment_plural),
QueryColumn.new(:description, :inline => false),
- QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false)
+ QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false),
+ QueryJournalsColumn.new(:id, :caption => :label_notes_id),
+ QueryJournalsColumn.new(:notes, :caption => :field_notes),
+ QueryJournalsColumn.new(:user, :caption => :label_notes_author),
+ QueryJournalsColumn.new(:created_on, :caption => :label_notes_created_on),
+ QueryJournalsColumn.new(:private_notes, :caption => :field_private_notes)
]
has_many :projects, foreign_key: 'default_issue_query_id', dependent: :nullify, inverse_of: 'default_issue_query'
@@ -328,6 +333,10 @@ class IssueQuery < Query
@available_columns
end
+ def self.journal_column_names
+ available_columns.select{|c| c.is_a?(QueryJournalsColumn)}.map(&:name)
+ end
+
def default_columns_names
@default_columns_names ||= begin
default_columns = Setting.issue_list_default_columns.map(&:to_sym)
@@ -410,8 +419,11 @@ class IssueQuery < Query
if has_column?(:relations)
Issue.load_visible_relations(issues)
end
+ if IssueQuery.journal_column_names.any?{|name| has_column?(name)}
+ Issue.load_visible_journals_with_notes(issues)
+ end
if has_column?(:last_notes)
- Issue.load_visible_last_notes(issues)
+ Issue.load_visible_last_notes(issues, User.current, IssueQuery.journal_column_names.any?{|name| has_column?(name)})
end
issues
rescue ::ActiveRecord::StatementInvalid => e
diff --git a/app/models/query.rb b/app/models/query.rb
index 0186cc379a..ad2d60e76c 100644
--- a/app/models/query.rb
+++ b/app/models/query.rb
@@ -62,6 +62,10 @@ class QueryColumn
@inline
end
+ def journal_notes?
+ false
+ end
+
def frozen?
@frozen
end
@@ -199,6 +203,25 @@ class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn
end
end
+class QueryJournalsColumn < QueryColumn
+ def initialize(name, options={})
+ @attribute = name
+ @inline = false
+ name_with_assoc = "journals.#{name}".to_sym
+ super(name_with_assoc, options)
+ end
+
+ def value_object(object)
+ return nil unless object.is_a?(Journal)
+
+ object.send @attribute
+ end
+
+ def journal_notes?
+ true
+ end
+end
+
class QueryFilter
include Redmine::I18n
@@ -798,18 +821,30 @@ class Query < ActiveRecord::Base
columns.reject(&:inline?)
end
+ def journal_notes_columns
+ columns.select(&:journal_notes?)
+ end
+
+ def without_journal_notes_columns
+ columns.reject(&:journal_notes?)
+ end
+
def available_inline_columns
available_columns.select(&:inline?)
end
def available_block_columns
- available_columns.reject(&:inline?)
+ available_columns.reject(&:inline?).reject(&:journal_notes?)
end
def available_totalable_columns
available_columns.select(&:totalable)
end
+ def available_journal_notes_columns
+ available_columns.select(&:journal_notes?)
+ end
+
def default_columns_names
[]
end
diff --git a/app/views/issues/index.html.erb b/app/views/issues/index.html.erb
index b05b6ccf2d..ac21024727 100644
--- a/app/views/issues/index.html.erb
+++ b/app/views/issues/index.html.erb
@@ -47,13 +47,18 @@