From 42181112ff73822afe5c0f96e065ae372f392e47 Mon Sep 17 00:00:00 2001
From: Jean-Philippe Lang
Date: Mon, 27 Nov 2006 22:31:14 +0000
Subject: [PATCH] improved issues change history
git-svn-id: http://redmine.rubyforge.org/svn/trunk@54 e93f8b46-1217-0410-a6f0-8f06a7374b81
---
redmine/app/controllers/application.rb | 21 ++++++-
redmine/app/controllers/issues_controller.rb | 40 ++++++++-----
.../app/controllers/projects_controller.rb | 1 +
redmine/app/helpers/application_helper.rb | 2 +-
redmine/app/helpers/custom_fields_helper.rb | 17 ++++--
redmine/app/helpers/ifpdf_helper.rb | 12 ++--
redmine/app/helpers/issues_helper.rb | 57 ++++++++++++++++++-
redmine/app/models/issue.rb | 45 ++++++++++++---
redmine/app/models/journal.rb | 22 +++++++
redmine/app/models/journal_detail.rb | 20 +++++++
redmine/app/models/mailer.rb | 18 ++----
redmine/app/views/issues/_history.rhtml | 11 ++++
redmine/app/views/issues/_pdf.rfpdf | 35 +++++++-----
redmine/app/views/issues/change_status.rhtml | 17 +++---
redmine/app/views/issues/edit.rhtml | 2 +-
redmine/app/views/issues/history.rhtml | 6 ++
redmine/app/views/issues/show.rhtml | 37 ++++++------
redmine/app/views/layouts/base.rhtml | 2 +-
.../app/views/mailer/issue_add_note_de.rhtml | 3 -
.../app/views/mailer/issue_add_note_en.rhtml | 3 -
.../app/views/mailer/issue_add_note_es.rhtml | 3 -
.../app/views/mailer/issue_add_note_fr.rhtml | 3 -
.../views/mailer/issue_change_status_de.rhtml | 3 -
.../views/mailer/issue_change_status_en.rhtml | 3 -
.../views/mailer/issue_change_status_es.rhtml | 3 -
.../views/mailer/issue_change_status_fr.rhtml | 3 -
redmine/app/views/mailer/issue_edit_de.rhtml | 8 +++
redmine/app/views/mailer/issue_edit_en.rhtml | 8 +++
redmine/app/views/mailer/issue_edit_es.rhtml | 8 +++
redmine/app/views/mailer/issue_edit_fr.rhtml | 8 +++
redmine/db/migrate/007_create_journals.rb | 54 ++++++++++++++++++
redmine/doc/CHANGELOG | 1 +
redmine/lang/de.yml | 5 ++
redmine/lang/en.yml | 5 ++
redmine/lang/es.yml | 5 ++
redmine/lang/fr.yml | 5 ++
redmine/public/stylesheets/application.css | 17 ++++--
redmine/test/fixtures/issue_histories.yml | 29 ----------
38 files changed, 389 insertions(+), 153 deletions(-)
create mode 100644 redmine/app/models/journal.rb
create mode 100644 redmine/app/models/journal_detail.rb
create mode 100644 redmine/app/views/issues/_history.rhtml
create mode 100644 redmine/app/views/issues/history.rhtml
delete mode 100644 redmine/app/views/mailer/issue_add_note_de.rhtml
delete mode 100644 redmine/app/views/mailer/issue_add_note_en.rhtml
delete mode 100644 redmine/app/views/mailer/issue_add_note_es.rhtml
delete mode 100644 redmine/app/views/mailer/issue_add_note_fr.rhtml
delete mode 100644 redmine/app/views/mailer/issue_change_status_de.rhtml
delete mode 100644 redmine/app/views/mailer/issue_change_status_en.rhtml
delete mode 100644 redmine/app/views/mailer/issue_change_status_es.rhtml
delete mode 100644 redmine/app/views/mailer/issue_change_status_fr.rhtml
create mode 100644 redmine/app/views/mailer/issue_edit_de.rhtml
create mode 100644 redmine/app/views/mailer/issue_edit_en.rhtml
create mode 100644 redmine/app/views/mailer/issue_edit_es.rhtml
create mode 100644 redmine/app/views/mailer/issue_edit_fr.rhtml
create mode 100644 redmine/db/migrate/007_create_journals.rb
delete mode 100644 redmine/test/fixtures/issue_histories.yml
diff --git a/redmine/app/controllers/application.rb b/redmine/app/controllers/application.rb
index e1e842b0b..3bebf4de5 100644
--- a/redmine/app/controllers/application.rb
+++ b/redmine/app/controllers/application.rb
@@ -41,7 +41,7 @@ class ApplicationController < ActionController::Base
if self.logged_in_user and self.logged_in_user.language and !self.logged_in_user.language.empty? and GLoc.valid_languages.include? self.logged_in_user.language.to_sym
self.logged_in_user.language
elsif request.env['HTTP_ACCEPT_LANGUAGE']
- accept_lang = HTTPUtils.parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
+ accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
if accept_lang and !accept_lang.empty? and GLoc.valid_languages.include? accept_lang.to_sym
accept_lang
end
@@ -104,4 +104,23 @@ class ApplicationController < ActionController::Base
session[:return_to] = nil
end
end
+
+ # qvalues http header parser
+ # code taken from webrick
+ def parse_qvalues(value)
+ tmp = []
+ if value
+ parts = value.split(/,\s*/)
+ parts.each {|part|
+ if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
+ val = m[1]
+ q = (m[2] or 1).to_f
+ tmp.push([val, q])
+ end
+ }
+ tmp = tmp.sort_by{|val, q| -q}
+ tmp.collect!{|val, q| val}
+ end
+ return tmp
+ end
end
\ No newline at end of file
diff --git a/redmine/app/controllers/issues_controller.rb b/redmine/app/controllers/issues_controller.rb
index 45b4fe633..e196e8226 100644
--- a/redmine/app/controllers/issues_controller.rb
+++ b/redmine/app/controllers/issues_controller.rb
@@ -27,6 +27,13 @@ class IssuesController < ApplicationController
def show
@status_options = @issue.status.workflows.find(:all, :include => :new_status, :conditions => ["role_id=? and tracker_id=?", self.logged_in_user.role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if self.logged_in_user
@custom_values = @issue.custom_values.find(:all, :include => :custom_field)
+ @journals_count = @issue.journals.count
+ @journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "journals.created_on desc")
+ end
+
+ def history
+ @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "journals.created_on desc")
+ @journals_count = @journals.length
end
def export_pdf
@@ -41,6 +48,7 @@ class IssuesController < ApplicationController
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
else
begin
+ @issue.init_journal(self.logged_in_user)
# Retrieve custom fields and values
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
@issue.custom_values = @custom_values
@@ -57,13 +65,14 @@ class IssuesController < ApplicationController
end
def add_note
- unless params[:history][:notes].empty?
- @history = @issue.histories.build(params[:history])
- @history.author_id = self.logged_in_user.id if self.logged_in_user
- @history.status = @issue.status
- if @history.save
+ unless params[:notes].empty?
+ journal = @issue.init_journal(self.logged_in_user, params[:notes])
+ #@history = @issue.histories.build(params[:history])
+ #@history.author_id = self.logged_in_user.id if self.logged_in_user
+ #@history.status = @issue.status
+ if @issue.save
flash[:notice] = l(:notice_successful_update)
- Mailer.deliver_issue_add_note(@history) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
+ Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @issue
return
end
@@ -73,17 +82,20 @@ class IssuesController < ApplicationController
end
def change_status
- @history = @issue.histories.build(params[:history])
+ #@history = @issue.histories.build(params[:history])
@status_options = @issue.status.workflows.find(:all, :conditions => ["role_id=? and tracker_id=?", self.logged_in_user.role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if self.logged_in_user
+ @new_status = IssueStatus.find(params[:new_status_id])
if params[:confirm]
begin
- @history.author_id = self.logged_in_user.id if self.logged_in_user
- @issue.status = @history.status
- @issue.fixed_version_id = (params[:issue][:fixed_version_id])
- @issue.assigned_to_id = (params[:issue][:assigned_to_id])
- @issue.done_ratio = (params[:issue][:done_ratio])
- @issue.lock_version = (params[:issue][:lock_version])
- if @issue.save
+ #@history.author_id = self.logged_in_user.id if self.logged_in_user
+ #@issue.status = @history.status
+ #@issue.fixed_version_id = (params[:issue][:fixed_version_id])
+ #@issue.assigned_to_id = (params[:issue][:assigned_to_id])
+ #@issue.done_ratio = (params[:issue][:done_ratio])
+ #@issue.lock_version = (params[:issue][:lock_version])
+ @issue.init_journal(self.logged_in_user, params[:notes])
+ @issue.status = @new_status
+ if @issue.update_attributes(params[:issue])
flash[:notice] = l(:notice_successful_update)
Mailer.deliver_issue_change_status(@issue) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @issue
diff --git a/redmine/app/controllers/projects_controller.rb b/redmine/app/controllers/projects_controller.rb
index a2b7abb4d..c968800c3 100644
--- a/redmine/app/controllers/projects_controller.rb
+++ b/redmine/app/controllers/projects_controller.rb
@@ -28,6 +28,7 @@ class ProjectsController < ApplicationController
include CustomFieldsHelper
helper :ifpdf
include IfpdfHelper
+ helper IssuesHelper
def index
list
diff --git a/redmine/app/helpers/application_helper.rb b/redmine/app/helpers/application_helper.rb
index 378545eb1..0f0deeb30 100644
--- a/redmine/app/helpers/application_helper.rb
+++ b/redmine/app/helpers/application_helper.rb
@@ -81,7 +81,7 @@ module ApplicationHelper
end
def textilizable(text)
- $RDM_TEXTILE_DISABLED ? text : textilize(text)
+ $RDM_TEXTILE_DISABLED ? text : RedCloth.new(text).to_html
end
def error_messages_for(object_name, options = {})
diff --git a/redmine/app/helpers/custom_fields_helper.rb b/redmine/app/helpers/custom_fields_helper.rb
index 38cb9df5e..9df5c50a3 100644
--- a/redmine/app/helpers/custom_fields_helper.rb
+++ b/redmine/app/helpers/custom_fields_helper.rb
@@ -54,15 +54,20 @@ module CustomFieldsHelper
# Return a string used to display a custom value
def show_value(custom_value)
return "" unless custom_value
-
- case custom_value.custom_field.field_format
+ format_value(custom_value.value, custom_value.custom_field.field_format)
+ end
+
+ # Return a string used to display a custom value
+ def format_value(value, field_format)
+ return "" unless value
+ case field_format
when "date"
- custom_value.value.empty? ? "" : l_date(custom_value.value.to_date)
+ value.empty? ? "" : l_date(value.to_date)
when "bool"
- l_YesNo(custom_value.value == "1")
+ l_YesNo(value == "1")
else
- custom_value.value
- end
+ value
+ end
end
# Return an array of custom field formats which can be used in select_tag
diff --git a/redmine/app/helpers/ifpdf_helper.rb b/redmine/app/helpers/ifpdf_helper.rb
index 4e7a173f4..a0dab0f94 100644
--- a/redmine/app/helpers/ifpdf_helper.rb
+++ b/redmine/app/helpers/ifpdf_helper.rb
@@ -25,12 +25,12 @@ module IfpdfHelper
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
@ic ||= Iconv.new('ISO-8859-1', 'UTF-8')
- super w,h,@ic.iconv(txt),border,ln,align,fill,link
- end
-
- def MultiCell(w,h,txt,border=0,align='J',fill=0)
- @ic ||= Iconv.new('ISO-8859-1', 'UTF-8')
- super w,h,txt,border,align,fill
+ txt = begin
+ @ic.iconv(txt)
+ rescue
+ txt
+ end
+ super w,h,txt,border,ln,align,fill,link
end
def Footer
diff --git a/redmine/app/helpers/issues_helper.rb b/redmine/app/helpers/issues_helper.rb
index 40c0e40ae..93bd6c050 100644
--- a/redmine/app/helpers/issues_helper.rb
+++ b/redmine/app/helpers/issues_helper.rb
@@ -15,5 +15,60 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-module IssuesHelper
+module IssuesHelper
+
+ def show_detail(detail, no_html=false)
+ case detail.property
+ when 'attr'
+ label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
+ case detail.prop_key
+ when 'due_date', 'start_date'
+ value = format_date(detail.value.to_date) if detail.value
+ old_value = format_date(detail.old_value.to_date) if detail.old_value
+ when 'status_id'
+ s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
+ s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
+ when 'assigned_to_id'
+ u = User.find_by_id(detail.value) and value = u.name if detail.value
+ u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
+ when 'priority_id'
+ e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
+ e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
+ when 'category_id'
+ c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
+ c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
+ when 'fixed_version_id'
+ v = Version.find_by_id(detail.value) and value = v.name if detail.value
+ v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
+ end
+ when 'cf'
+ custom_field = CustomField.find_by_id(detail.prop_key)
+ if custom_field
+ label = custom_field.name
+ value = format_value(detail.value, custom_field.field_format) if detail.value
+ old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
+ end
+ end
+
+ label ||= detail.prop_key
+ value ||= detail.value
+ old_value ||= detail.old_value
+
+ unless no_html
+ label = content_tag('strong', label)
+ old_value = content_tag("i", old_value) if old_value
+ old_value = content_tag("strike", old_value) if old_value and !value
+ value = content_tag("i", value) if value
+ end
+
+ if value
+ if old_value
+ label + " " + l(:text_journal_changed, old_value, value)
+ else
+ label + " " + l(:text_journal_set_to, value)
+ end
+ else
+ label + " " + l(:text_journal_deleted) + " (#{old_value})"
+ end
+ end
end
diff --git a/redmine/app/models/issue.rb b/redmine/app/models/issue.rb
index 4834e069f..f00eb7a9c 100644
--- a/redmine/app/models/issue.rb
+++ b/redmine/app/models/issue.rb
@@ -26,7 +26,8 @@ class Issue < ActiveRecord::Base
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
- has_many :histories, :class_name => 'IssueHistory', :dependent => true, :order => "issue_histories.created_on DESC", :include => :status
+ #has_many :histories, :class_name => 'IssueHistory', :dependent => true, :order => "issue_histories.created_on DESC", :include => :status
+ has_many :journals, :as => :journalized, :dependent => true
has_many :attachments, :as => :container, :dependent => true
has_many :custom_values, :dependent => true, :as => :customized
@@ -51,8 +52,28 @@ class Issue < ActiveRecord::Base
end
end
- def before_create
- build_history
+ #def before_create
+ # build_history
+ #end
+
+ def before_save
+ if @current_journal
+ # attributes changes
+ (Issue.column_names - %w(id description)).each {|c|
+ @current_journal.details << JournalDetail.new(:property => 'attr',
+ :prop_key => c,
+ :old_value => @issue_before_change.send(c),
+ :value => send(c)) unless send(c)==@issue_before_change.send(c)
+ }
+ # custom fields changes
+ custom_values.each {|c|
+ @current_journal.details << JournalDetail.new(:property => 'cf',
+ :prop_key => c.custom_field_id,
+ :old_value => @custom_values_before_change[c.custom_field_id],
+ :value => c.value) unless @custom_values_before_change[c.custom_field_id]==c.value
+ }
+ @current_journal.save unless @current_journal.details.empty? and @current_journal.notes.empty?
+ end
end
def long_id
@@ -63,12 +84,20 @@ class Issue < ActiveRecord::Base
self.custom_values.each {|v| return v if v.custom_field_id == custom_field.id }
return nil
end
+
+ def init_journal(user, notes = "")
+ @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
+ @issue_before_change = self.clone
+ @custom_values_before_change = {}
+ self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
+ @current_journal
+ end
private
# Creates an history for the issue
- def build_history
- @history = self.histories.build
- @history.status = self.status
- @history.author = self.author
- end
+ #def build_history
+ # @history = self.histories.build
+ # @history.status = self.status
+ # @history.author = self.author
+ #end
end
diff --git a/redmine/app/models/journal.rb b/redmine/app/models/journal.rb
new file mode 100644
index 000000000..9d173552f
--- /dev/null
+++ b/redmine/app/models/journal.rb
@@ -0,0 +1,22 @@
+# redMine - project management software
+# Copyright (C) 2006 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Journal < ActiveRecord::Base
+ belongs_to :journalized, :polymorphic => true
+ belongs_to :user
+ has_many :details, :class_name => "JournalDetail", :dependent => true
+end
diff --git a/redmine/app/models/journal_detail.rb b/redmine/app/models/journal_detail.rb
new file mode 100644
index 000000000..784e98bf7
--- /dev/null
+++ b/redmine/app/models/journal_detail.rb
@@ -0,0 +1,20 @@
+# redMine - project management software
+# Copyright (C) 2006 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class JournalDetail < ActiveRecord::Base
+ belongs_to :journal
+end
diff --git a/redmine/app/models/mailer.rb b/redmine/app/models/mailer.rb
index fa19bf4da..bbe782526 100644
--- a/redmine/app/models/mailer.rb
+++ b/redmine/app/models/mailer.rb
@@ -17,14 +17,6 @@
class Mailer < ActionMailer::Base
- def issue_change_status(issue)
- # Sends to all project members
- @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
- @from = $RDM_MAIL_FROM
- @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
- @body['issue'] = issue
- end
-
def issue_add(issue)
# Sends to all project members
@recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
@@ -33,12 +25,14 @@ class Mailer < ActionMailer::Base
@body['issue'] = issue
end
- def issue_add_note(history)
+ def issue_edit(journal)
# Sends to all project members
- @recipients = history.issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
+ issue = journal.journalized
+ @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
@from = $RDM_MAIL_FROM
- @subject = "[#{history.issue.project.name} - #{history.issue.tracker.name} ##{history.issue.id}] #{history.issue.status.name} - #{history.issue.subject}"
- @body['history'] = history
+ @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
+ @body['issue'] = issue
+ @body['journal']= journal
end
def lost_password(token)
diff --git a/redmine/app/views/issues/_history.rhtml b/redmine/app/views/issues/_history.rhtml
new file mode 100644
index 000000000..6dc2a84be
--- /dev/null
+++ b/redmine/app/views/issues/_history.rhtml
@@ -0,0 +1,11 @@
+<% for journal in journals %>
+ <%= format_time(journal.created_on) %> - <%= journal.user.name %>
+
+ <% for detail in journal.details %>
+ <%= show_detail(detail) %>
+ <% end %>
+
+ <% if journal.notes? %>
+ <%= simple_format auto_link journal.notes %>
+ <% end %>
+<% end %>
diff --git a/redmine/app/views/issues/_pdf.rfpdf b/redmine/app/views/issues/_pdf.rfpdf
index d12db0a23..1f6a12283 100644
--- a/redmine/app/views/issues/_pdf.rfpdf
+++ b/redmine/app/views/issues/_pdf.rfpdf
@@ -66,29 +66,34 @@
pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
pdf.Ln
+
pdf.SetFont('Arial','B',9)
- pdf.Cell(190,5, l(:label_history),"B")
- pdf.Ln
- for history in issue.histories.find(:all, :include => [:author, :status])
- pdf.SetFont('Arial','B',8)
- pdf.Cell(100,5, history.status.name)
- pdf.SetFont('Arial','',8)
- pdf.Cell(20,5, format_date(history.created_on))
- pdf.Cell(70,5, history.author.name,0,0,"R")
- pdf.SetFont('Arial','',8)
+ pdf.Cell(190,5, l(:label_history), "B")
+ pdf.Ln
+ for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc")
+ pdf.SetFont('Arial','B',8)
+ pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
pdf.Ln
- pdf.Cell(10,4, "") and pdf.MultiCell(180,4, history.notes) if history.notes?
+ pdf.SetFont('Arial','I',8)
+ for detail in journal.details
+ pdf.Cell(190,5, "- " + show_detail(detail, true))
+ pdf.Ln
+ end
+ if journal.notes?
+ pdf.SetFont('Arial','',8)
+ pdf.MultiCell(190,5, journal.notes)
+ end
+ pdf.Ln
end
- pdf.Ln
-
+
pdf.SetFont('Arial','B',9)
pdf.Cell(190,5, l(:label_attachment_plural), "B")
- pdf.Ln
+ pdf.Ln
for attachment in issue.attachments
pdf.SetFont('Arial','',8)
pdf.Cell(80,5, attachment.filename)
- pdf.Cell(20,5, human_size(attachment.filesize))
- pdf.Cell(20,5, format_date(attachment.created_on))
+ pdf.Cell(20,5, human_size(attachment.filesize),0,0,"R")
+ pdf.Cell(20,5, format_date(attachment.created_on),0,0,"R")
pdf.Cell(70,5, attachment.author.name,0,0,"R")
pdf.Ln
end
diff --git a/redmine/app/views/issues/change_status.rhtml b/redmine/app/views/issues/change_status.rhtml
index 0fe07ad23..2ef87183d 100644
--- a/redmine/app/views/issues/change_status.rhtml
+++ b/redmine/app/views/issues/change_status.rhtml
@@ -1,13 +1,13 @@
<%=l(:label_issue)%> #<%= @issue.id %>: <%= @issue.subject %>
-<%= error_messages_for 'history' %>
+<%= error_messages_for 'issue' %>
<%= start_form_tag({:action => 'change_status', :id => @issue}, :class => "tabular") %>
<%= hidden_field_tag 'confirm', 1 %>
-<%= hidden_field 'history', 'status_id' %>
+<%= hidden_field_tag 'new_status_id', @new_status.id %>
-
<%=l(:label_issue_status_new)%> <%= @history.status.name %>
+
<%=l(:label_issue_status_new)%> <%= @new_status.name %>
<%=l(:field_assigned_to)%>
@@ -25,12 +25,13 @@
--none--
<%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %>
-
-
-
<%=l(:field_notes)%>
-<%= text_area 'history', 'notes', :cols => 60, :rows => 10 %>
-
+
+<%= l(:field_notes) %>
+<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10 %>
+
+
+
<%= hidden_field 'issue', 'lock_version' %>
<%= submit_tag l(:button_save) %>
<%= end_form_tag %>
diff --git a/redmine/app/views/issues/edit.rhtml b/redmine/app/views/issues/edit.rhtml
index 6044fa9fd..60cdafc1e 100644
--- a/redmine/app/views/issues/edit.rhtml
+++ b/redmine/app/views/issues/edit.rhtml
@@ -19,7 +19,7 @@
<%= f.text_field :subject, :size => 80, :required => true %>
-
<%= f.text_area :description, :cols => 60, :rows => 10, :required => true %>
+
<%= f.text_area :description, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :required => true %>
<% for @custom_value in @custom_values %>
<%= custom_field_tag_with_label @custom_value %>
diff --git a/redmine/app/views/issues/history.rhtml b/redmine/app/views/issues/history.rhtml
new file mode 100644
index 000000000..2443cc739
--- /dev/null
+++ b/redmine/app/views/issues/history.rhtml
@@ -0,0 +1,6 @@
+
<%=l(:label_history)%>
+
+<%= render :partial => 'history', :locals => { :journals => @journals } %>
+
+
+
<%= link_to l(:button_back), :action => 'show', :id => @issue %>
\ No newline at end of file
diff --git a/redmine/app/views/issues/show.rhtml b/redmine/app/views/issues/show.rhtml
index 43b7d0449..8128b74a9 100644
--- a/redmine/app/views/issues/show.rhtml
+++ b/redmine/app/views/issues/show.rhtml
@@ -44,8 +44,8 @@ end %>
<%=l(:field_description)%> :
<%= textilizable @issue.description %>
-
-
+
+
<% if authorize_for('issues', 'edit') %>
<%= start_form_tag ({:controller => 'issues', :action => 'edit', :id => @issue}, :method => "get" ) %>
<%= submit_tag l(:button_edit) %>
@@ -56,7 +56,7 @@ end %>
<% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %>
<%= start_form_tag ({:controller => 'issues', :action => 'change_status', :id => @issue}) %>
<%=l(:label_change_status)%> :
-
+
<%= options_from_collection_for_select @status_options, "id", "name" %>
<%= submit_tag l(:button_change) %>
@@ -71,30 +71,25 @@ end %>
<%= end_form_tag %>
<% end %>
-
+
+
<% if authorize_for('issues', 'destroy') %>
<%= start_form_tag ({:controller => 'issues', :action => 'destroy', :id => @issue} ) %>
<%= submit_tag l(:button_delete) %>
<%= end_form_tag %>
<% end %>
-
+
+
-
-
<%=l(:label_history)%>
-
-<% for history in @issue.histories.find(:all, :include => [:author, :status]) %>
-
-<%= format_date(history.created_on) %>
-<%= history.author.display_name %>
-<%= history.status.name %>
-
-<% if history.notes? %>
- <%= simple_format auto_link history.notes %>
-<% end %>
+
+
<%=l(:label_history)%>
+<% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %>
+<%= render :partial => 'history', :locals => { :journals => @journals } %>
+<% if @journals_count > @journals.length %>
+
[ <%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %> ]
<% end %>
-
@@ -130,9 +125,9 @@ end %>
<%= l(:label_add_note) %>
<%= start_form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) %>
-
<%=l(:field_notes)%>
- <%= text_area 'history', 'notes', :cols => 60, :rows => 10 %>
+
<%=l(:field_notes)%>
+ <%= text_area_tag 'notes', '', :cols => 60, :rows => 10 %>
<%= submit_tag l(:button_add) %>
- <%= end_form_tag %>
+ <%= end_form_tag %>
<% end %>
diff --git a/redmine/app/views/layouts/base.rhtml b/redmine/app/views/layouts/base.rhtml
index e04d3cdea..767f51f61 100644
--- a/redmine/app/views/layouts/base.rhtml
+++ b/redmine/app/views/layouts/base.rhtml
@@ -133,7 +133,7 @@ var menu_contenu=' \
diff --git a/redmine/app/views/mailer/issue_add_note_de.rhtml b/redmine/app/views/mailer/issue_add_note_de.rhtml
deleted file mode 100644
index 9942c2440..000000000
--- a/redmine/app/views/mailer/issue_add_note_de.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Note added to issue #<%= @history.issue_id %> by <%= @history.author.name %>
-----------------------------------------
-<%= @history.notes %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_add_note_en.rhtml b/redmine/app/views/mailer/issue_add_note_en.rhtml
deleted file mode 100644
index 9942c2440..000000000
--- a/redmine/app/views/mailer/issue_add_note_en.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Note added to issue #<%= @history.issue_id %> by <%= @history.author.name %>
-----------------------------------------
-<%= @history.notes %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_add_note_es.rhtml b/redmine/app/views/mailer/issue_add_note_es.rhtml
deleted file mode 100644
index 9942c2440..000000000
--- a/redmine/app/views/mailer/issue_add_note_es.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Note added to issue #<%= @history.issue_id %> by <%= @history.author.name %>
-----------------------------------------
-<%= @history.notes %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_add_note_fr.rhtml b/redmine/app/views/mailer/issue_add_note_fr.rhtml
deleted file mode 100644
index 0534cc177..000000000
--- a/redmine/app/views/mailer/issue_add_note_fr.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Note ajoutée à la demande #<%= @history.issue_id %> par <%= @history.author.name %>
-----------------------------------------
-<%= @history.notes %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_change_status_de.rhtml b/redmine/app/views/mailer/issue_change_status_de.rhtml
deleted file mode 100644
index e3612ea81..000000000
--- a/redmine/app/views/mailer/issue_change_status_de.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Issue #<%= @issue.id %> has been updated to "<%= @issue.status.name %>" status.
-----------------------------------------
-<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_change_status_en.rhtml b/redmine/app/views/mailer/issue_change_status_en.rhtml
deleted file mode 100644
index e3612ea81..000000000
--- a/redmine/app/views/mailer/issue_change_status_en.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Issue #<%= @issue.id %> has been updated to "<%= @issue.status.name %>" status.
-----------------------------------------
-<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_change_status_es.rhtml b/redmine/app/views/mailer/issue_change_status_es.rhtml
deleted file mode 100644
index e3612ea81..000000000
--- a/redmine/app/views/mailer/issue_change_status_es.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-Issue #<%= @issue.id %> has been updated to "<%= @issue.status.name %>" status.
-----------------------------------------
-<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_change_status_fr.rhtml b/redmine/app/views/mailer/issue_change_status_fr.rhtml
deleted file mode 100644
index 831f301d5..000000000
--- a/redmine/app/views/mailer/issue_change_status_fr.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-La demande #<%= @issue.id %> a été mise à jour au statut "<%= @issue.status.name %>".
-----------------------------------------
-<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_edit_de.rhtml b/redmine/app/views/mailer/issue_edit_de.rhtml
new file mode 100644
index 000000000..1b97a571b
--- /dev/null
+++ b/redmine/app/views/mailer/issue_edit_de.rhtml
@@ -0,0 +1,8 @@
+Issue #<%= @issue.id %> has been updated.
+<%= @journal.user.name %>
+<% for detail in @journal.details %>
+<%= show_detail(detail) %>
+<% end %>
+<%= @journal.notes if @journal.notes? %>
+----------------------------------------
+<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_edit_en.rhtml b/redmine/app/views/mailer/issue_edit_en.rhtml
new file mode 100644
index 000000000..1b97a571b
--- /dev/null
+++ b/redmine/app/views/mailer/issue_edit_en.rhtml
@@ -0,0 +1,8 @@
+Issue #<%= @issue.id %> has been updated.
+<%= @journal.user.name %>
+<% for detail in @journal.details %>
+<%= show_detail(detail) %>
+<% end %>
+<%= @journal.notes if @journal.notes? %>
+----------------------------------------
+<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_edit_es.rhtml b/redmine/app/views/mailer/issue_edit_es.rhtml
new file mode 100644
index 000000000..1b97a571b
--- /dev/null
+++ b/redmine/app/views/mailer/issue_edit_es.rhtml
@@ -0,0 +1,8 @@
+Issue #<%= @issue.id %> has been updated.
+<%= @journal.user.name %>
+<% for detail in @journal.details %>
+<%= show_detail(detail) %>
+<% end %>
+<%= @journal.notes if @journal.notes? %>
+----------------------------------------
+<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/app/views/mailer/issue_edit_fr.rhtml b/redmine/app/views/mailer/issue_edit_fr.rhtml
new file mode 100644
index 000000000..b7c3a50a3
--- /dev/null
+++ b/redmine/app/views/mailer/issue_edit_fr.rhtml
@@ -0,0 +1,8 @@
+La demande #<%= @issue.id %> a été mise à jour.
+<%= @journal.user.name %> - <%= format_date(@journal.created_on) %>
+<% for detail in @journal.details %>
+<%= show_detail(detail) %>
+<% end %>
+<%= journal.notes if journal.notes? %>
+----------------------------------------
+<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
diff --git a/redmine/db/migrate/007_create_journals.rb b/redmine/db/migrate/007_create_journals.rb
new file mode 100644
index 000000000..6170b5bd3
--- /dev/null
+++ b/redmine/db/migrate/007_create_journals.rb
@@ -0,0 +1,54 @@
+class CreateJournals < ActiveRecord::Migration
+
+ # model removed, but needed for data migration
+ class IssueHistory < ActiveRecord::Base; belongs_to :issue; end
+
+ def self.up
+ create_table :journals, :force => true do |t|
+ t.column "journalized_id", :integer, :default => 0, :null => false
+ t.column "journalized_type", :string, :limit => 30, :default => "", :null => false
+ t.column "user_id", :integer, :default => 0, :null => false
+ t.column "notes", :text
+ t.column "created_on", :datetime, :null => false
+ end
+ create_table :journal_details, :force => true do |t|
+ t.column "journal_id", :integer, :default => 0, :null => false
+ t.column "property", :string, :limit => 30, :default => "", :null => false
+ t.column "prop_key", :string, :limit => 30, :default => "", :null => false
+ t.column "old_value", :string
+ t.column "value", :string
+ end
+
+ # indexes
+ add_index "journals", ["journalized_id", "journalized_type"], :name => "journals_journalized_id"
+ add_index "journal_details", ["journal_id"], :name => "journal_details_journal_id"
+
+ Permission.create :controller => "issues", :action => "history", :description => "label_history", :sort => 1006, :is_public => true, :mail_option => 0, :mail_enabled => 0
+
+ # data migration
+ IssueHistory.find(:all, :include => :issue).each {|h|
+ j = Journal.new(:journalized => h.issue, :user_id => h.author_id, :notes => h.notes, :created_on => h.created_on)
+ j.details << JournalDetail.new(:property => 'attr', :prop_key => 'status_id', :value => h.status_id)
+ j.save
+ }
+
+ drop_table :issue_histories
+ end
+
+ def self.down
+ drop_table :journal_details
+ drop_table :journals
+
+ create_table "issue_histories", :force => true do |t|
+ t.column "issue_id", :integer, :default => 0, :null => false
+ t.column "status_id", :integer, :default => 0, :null => false
+ t.column "author_id", :integer, :default => 0, :null => false
+ t.column "notes", :text, :default => ""
+ t.column "created_on", :timestamp
+ end
+
+ add_index "issue_histories", ["issue_id"], :name => "issue_histories_issue_id"
+
+ Permission.find(:first, :conditions => ["controller=? and action=?", 'issues', 'history']).destroy
+ end
+end
diff --git a/redmine/doc/CHANGELOG b/redmine/doc/CHANGELOG
index f310f63c3..bbf8a4898 100644
--- a/redmine/doc/CHANGELOG
+++ b/redmine/doc/CHANGELOG
@@ -7,6 +7,7 @@ http://redmine.org/
== xx/xx/2006 v0.x.x
+* improved issues change history
* new functionality: move an issue to another project or tracker
* new functionality: add a note to an issue
* new report: project activity
diff --git a/redmine/lang/de.yml b/redmine/lang/de.yml
index bc10248d8..709ca18da 100644
--- a/redmine/lang/de.yml
+++ b/redmine/lang/de.yml
@@ -256,6 +256,8 @@ label_calendar: Kalender
label_months_from: Monate von
label_gantt_chart: Gantt Diagramm
label_internal: Intern
+label_last_changes: %d änderungen des Letzten
+label_change_view_all: Alle änderungen ansehen
button_login: Einloggen
button_submit: Einreichen
@@ -285,6 +287,9 @@ text_possible_values_info: Werte trennten sich mit |
text_project_destroy_confirmation: Sind sie sicher, daà sie das Projekt löschen wollen ?
text_workflow_edit: Auswahl Workflow zum Bearbeiten
text_are_you_sure: Sind sie sicher ?
+text_journal_changed: geändert von %s zu %s
+text_journal_set_to: gestellt zu %s
+text_journal_deleted: gelöscht
default_role_manager: Manager
default_role_developper: Developer
diff --git a/redmine/lang/en.yml b/redmine/lang/en.yml
index e30e6c7a0..27cc0260f 100644
--- a/redmine/lang/en.yml
+++ b/redmine/lang/en.yml
@@ -256,6 +256,8 @@ label_calendar: Calendar
label_months_from: months from
label_gantt_chart: Gantt chart
label_internal: Internal
+label_last_changes: last %d changes
+label_change_view_all: View all changes
button_login: Login
button_submit: Submit
@@ -285,6 +287,9 @@ text_possible_values_info: values separated with |
text_project_destroy_confirmation: Are you sure you want to delete this project and all related data ?
text_workflow_edit: Select a role and a tracker to edit the workflow
text_are_you_sure: Are you sure ?
+text_journal_changed: changed from %s to %s
+text_journal_set_to: set to %s
+text_journal_deleted: deleted
default_role_manager: Manager
default_role_developper: Developer
diff --git a/redmine/lang/es.yml b/redmine/lang/es.yml
index 7c1c6e122..654efd73a 100644
--- a/redmine/lang/es.yml
+++ b/redmine/lang/es.yml
@@ -256,6 +256,8 @@ label_calendar: Calendario
label_months_from: meses de
label_gantt_chart: Diagrama de Gantt
label_internal: Interno
+label_last_changes: %d cambios del último
+label_change_view_all: Ver todos los cambios
button_login: Conexión
button_submit: Someter
@@ -285,6 +287,9 @@ text_possible_values_info: Los valores se separaron con |
text_project_destroy_confirmation: ¿ Estás seguro de querer eliminar el proyecto ?
text_workflow_edit: Seleccionar un workflow para actualizar
text_are_you_sure: ¿ Estás seguro ?
+text_journal_changed: cambiado de %s a %s
+text_journal_set_to: fijado a %s
+text_journal_deleted: suprimido
default_role_manager: Manager
default_role_developper: Desarrollador
diff --git a/redmine/lang/fr.yml b/redmine/lang/fr.yml
index 5802558ab..5973f1d3a 100644
--- a/redmine/lang/fr.yml
+++ b/redmine/lang/fr.yml
@@ -257,6 +257,8 @@ label_calendar: Calendrier
label_months_from: mois depuis
label_gantt_chart: Diagramme de Gantt
label_internal: Interne
+label_last_changes: %d derniers changements
+label_change_view_all: Voir tous les changements
button_login: Connexion
button_submit: Soumettre
@@ -286,6 +288,9 @@ text_possible_values_info: valeurs séparées par |
text_project_destroy_confirmation: Etes-vous sûr de vouloir supprimer ce projet et tout ce qui lui est rattaché ?
text_workflow_edit: Sélectionner un tracker et un rôle pour éditer le workflow
text_are_you_sure: Etes-vous sûr ?
+text_journal_changed: changé de %s à %s
+text_journal_set_to: mis à %s
+text_journal_deleted: supprimé
default_role_manager: Manager
default_role_developper: Développeur
diff --git a/redmine/public/stylesheets/application.css b/redmine/public/stylesheets/application.css
index c380b2c3a..bd128a3a7 100644
--- a/redmine/public/stylesheets/application.css
+++ b/redmine/public/stylesheets/application.css
@@ -186,10 +186,6 @@ form {
.noborder {
border:0px;
-Exception exceptions.AssertionError:
in >
-ignored
-
background-color:#fff;
width:100%;
}
@@ -292,7 +288,7 @@ table.calenderTable td {
border:1px solid #578bb8;
}
-hr { border:none; border-bottom: dotted 2px #c0c0c0; }
+hr { border:none; border-bottom: dotted 1px #c0c0c0; }
/**************** Sidebar styles ****************/
@@ -409,6 +405,17 @@ img.calendar-trigger {
margin-left: 4px;
}
+#history h4 {
+ font-size: 1em;
+ margin-bottom: 12px;
+ margin-top: 20px;
+ font-weight: normal;
+ border-bottom: dotted 1px #c0c0c0;
+}
+
+#history p {
+ margin-left: 34px;
+}
/***** CSS FORM ******/
.tabular p{
diff --git a/redmine/test/fixtures/issue_histories.yml b/redmine/test/fixtures/issue_histories.yml
deleted file mode 100644
index 5b7803650..000000000
--- a/redmine/test/fixtures/issue_histories.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-issue_histories_003:
- created_on: 2006-07-19 21:07:27 +02:00
- notes:
- issue_id: 3
- id: 3
- author_id: 2
- status_id: 1
-issue_histories_004:
- created_on: 2006-07-19 21:09:50 +02:00
- notes: Should be bone quickly
- issue_id: 2
- id: 4
- author_id: 2
- status_id: 2
-issue_histories_001:
- created_on: 2006-07-19 21:02:17 +02:00
- notes:
- issue_id: 1
- id: 1
- author_id: 2
- status_id: 1
-issue_histories_002:
- created_on: 2006-07-19 21:04:21 +02:00
- notes:
- issue_id: 2
- id: 2
- author_id: 2
- status_id: 1
--
2.39.5