diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb old mode 100644 new mode 100755 index 5c4ad24..4644cff --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -183,6 +183,8 @@ module QueriesHelper value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : '' when :description item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : '' + when :last_notes + item.last_notes.present? ? content_tag('div', textilizable(item, :last_notes), :class => "wiki") : '' when :done_ratio progress_bar(value) when :relations diff --git a/app/models/issue.rb b/app/models/issue.rb old mode 100644 new mode 100755 index 65d0784..691767e --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -244,6 +244,7 @@ class Issue < ActiveRecord::Base @spent_hours = nil @total_spent_hours = nil @total_estimated_hours = nil + @last_notes = nil base_reload(*args) end @@ -1048,6 +1049,16 @@ class Issue < ActiveRecord::Base @relations ||= IssueRelation::Relations.new(self, (relations_from + relations_to).sort) end + def last_notes + if @last_notes + @last_notes + else + notes = self.journals.where.not(notes: '').to_a + notes.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, self.project) + notes.last.notes unless notes.empty? + end + end + # Preloads relations for a collection of issues def self.load_relations(issues) if issues.any? @@ -1102,6 +1113,22 @@ class Issue < ActiveRecord::Base end end + # Preloads visible last notes for a collection of issues + def self.load_visible_last_notes(issues, user=User.current) + if issues.any? + issue_ids = issues.map(&:id) + logger.debug("="*30) + notes = Journal.joins(issue: :project).where.not(notes: ''). + where(:issues => {:id => issue_ids}).order("#{Journal.table_name}.id ASC").to_a + + issues.each do |issue| + note = notes.select{|note| note.journalized_id == issue.id} + note.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, issue.project) + issue.instance_variable_set "@last_notes", (note.empty? ? '' : note.last.notes) + end + 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 old mode 100644 new mode 100755 index 137764e..81ede65 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -44,7 +44,8 @@ class IssueQuery < Query QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'), QueryColumn.new(:relations, :caption => :label_related_issues), - QueryColumn.new(:description, :inline => false) + QueryColumn.new(:description, :inline => false), + QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false) ] def initialize(attributes=nil, *args) @@ -330,6 +331,9 @@ class IssueQuery < Query if has_column?(:relations) Issue.load_visible_relations(issues) end + if has_column?(:last_notes) + Issue.load_visible_last_notes(issues) + end issues rescue ::ActiveRecord::StatementInvalid => e raise StatementInvalid.new(e.message) diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb old mode 100644 new mode 100755 index 851fca1..1cb3a88 --- a/app/views/issues/_list.html.erb +++ b/app/views/issues/_list.html.erb @@ -33,7 +33,12 @@ <% @query.block_columns.each do |column| if (text = column_content(column, issue)) && text.present? -%> - <%= text %> + + <% if query.block_columns.count > 1 %> + <%= column.caption %> + <% end %> + <%= text %> + <% end -%> <% end -%> diff --git a/app/views/issues/index.html.erb b/app/views/issues/index.html.erb old mode 100644 new mode 100755 index a9f321c..e851d4e --- a/app/views/issues/index.html.erb +++ b/app/views/issues/index.html.erb @@ -37,6 +37,7 @@

+

<% if @issue_count > Setting.issues_export_limit.to_i %>

diff --git a/app/views/timelog/_list.html.erb b/app/views/timelog/_list.html.erb old mode 100644 new mode 100755 index 3721df2..a03039b --- a/app/views/timelog/_list.html.erb +++ b/app/views/timelog/_list.html.erb @@ -50,7 +50,12 @@ <% @query.block_columns.each do |column| if (text = column_content(column, issue)) && text.present? -%> - <%= text %> + + <% if query.block_columns.count > 1 %> + <%= column.caption %> + <% end %> + <%= text %> + <% end -%> <% end -%> diff --git a/config/locales/en.yml b/config/locales/en.yml old mode 100644 new mode 100755 index b5f771d..b31d678 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1009,6 +1009,7 @@ en: label_font_default: Default font label_font_monospace: Monospaced font label_font_proportional: Proportional font + label_last_notes: Last notes button_login: Login button_submit: Submit diff --git a/lib/redmine/export/pdf/issues_pdf_helper.rb b/lib/redmine/export/pdf/issues_pdf_helper.rb old mode 100644 new mode 100755 index 75f63d2..c5fffa8 --- a/lib/redmine/export/pdf/issues_pdf_helper.rb +++ b/lib/redmine/export/pdf/issues_pdf_helper.rb @@ -266,8 +266,8 @@ module Redmine table_width = col_width.inject(0, :+) end - # use full width if the description is displayed - if table_width > 0 && query.has_column?(:description) + # use full width if the description or last_notes are displayed + if table_width > 0 && (query.has_column?(:description) || query.has_column?(:last_notes)) col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width} table_width = col_width.inject(0, :+) end @@ -327,6 +327,13 @@ module Redmine pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.description.to_s, issue.attachments, "LRBT") pdf.set_auto_page_break(false) end + + if query.has_column?(:last_notes) && issue.last_notes.present? + pdf.set_x(10) + pdf.set_auto_page_break(true, bottom_margin) + pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.last_notes.to_s, [], "LRBT") + pdf.set_auto_page_break(false) + end end if issues.size == Setting.issues_export_limit.to_i diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css old mode 100644 new mode 100755 index 701be55..5d6341e --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -255,8 +255,8 @@ tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, t tr.issue td.relations { text-align: left; } tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;} tr.issue td.relations span {white-space: nowrap;} -table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;} -table.issues td.description pre {white-space:normal;} +table.issues td.description, table.issues td.last_notes {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;} +table.issues td.description pre, table.issues td.last_notes pre {white-space:normal;} tr.issue.idnt td.subject {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%;} tr.issue.idnt-1 td.subject {padding-left: 24px; background-position: 8px 50%;} diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb old mode 100644 new mode 100755 index 3e1bfa2..5570b2e --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -959,6 +959,43 @@ class IssuesControllerTest < Redmine::ControllerTest assert_equal 'application/pdf', response.content_type end + def test_index_with_last_notes_column + get :index, :set_filter => 1, :c => %w(subject last_notes) + + assert_response :success + assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject + + assert_select 'td.last_notes[colspan="3"]', :text => 'Some notes with Redmine links: #2, r2.' + assert_select 'td.last_notes[colspan="3"]', :text => 'A comment with inline image: and a reference to #1 and r2.' + + get :index, :set_filter => 1, :c => %w(subject last_notes), :format => 'pdf' + assert_response :success + assert_equal 'application/pdf', response.content_type + end + + def test_index_with_last_notes_column_should_display_private_notes_with_permission_only + journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1) + @request.session[:user_id] = 2 + + get :index, :set_filter => 1, :c => %w(subject last_notes) + assert_response :success + assert_select 'td.last_notes[colspan="3"]', :text => 'Privates notes' + + Role.find(1).remove_permission! :view_private_notes + + get :index, :set_filter => 1, :c => %w(subject last_notes) + assert_response :success + assert_select 'td.last_notes[colspan="3"]', :text => 'A comment with inline image: and a reference to #1 and r2.' + end + + def test_index_with_description_and_last_notes_columns_should_display_column_name + get :index, :set_filter => 1, :c => %w(subject last_notes description) + assert_response :success + + assert_select 'td.last_notes[colspan="3"] span', :text => 'Last notes' + assert_select 'td.description[colspan="3"] span', :text => 'Description' + end + def test_index_with_parent_column Issue.delete_all parent = Issue.generate! diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb old mode 100644 new mode 100755 index 6d27106..7b3ced0 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -1107,10 +1107,10 @@ class QueryTest < ActiveSupport::TestCase def test_inline_and_block_columns q = IssueQuery.new - q.column_names = ['subject', 'description', 'tracker'] + q.column_names = ['subject', 'description', 'tracker', 'last_notes'] assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name) - assert_equal [:description], q.block_columns.map(&:name) + assert_equal [:description, :last_notes], q.block_columns.map(&:name) end def test_custom_field_columns_should_be_inline @@ -1127,6 +1127,13 @@ class QueryTest < ActiveSupport::TestCase assert_not_nil issues.first.instance_variable_get("@spent_hours") end + def test_query_should_preload_last_notes + q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_notes]) + assert q.has_column?(:last_notes) + issues = q.issues + assert_not_nil issues.first.instance_variable_get("@last_notes") + end + def test_groupable_columns_should_include_custom_fields q = IssueQuery.new column = q.groupable_columns.detect {|c| c.name == :cf_1}