From 42181112ff73822afe5c0f96e065ae372f392e47 2006-11-27 22:31:14
From: Jean-Philippe Lang
Date: 2006-11-27 22:31:14
Subject: [PATCH] improved issues change history
git-svn-id: http://redmine.rubyforge.org/svn/trunk@54 e93f8b46-1217-0410-a6f0-8f06a7374b81
---
diff --git a/redmine/app/controllers/application.rb b/redmine/app/controllers/application.rb
index e1e842b..3bebf4d 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 45b4fe6..e196e82 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 a2b7abb..c968800 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 378545e..0f0deeb 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 38cb9df..9df5c50 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 4e7a173..a0dab0f 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 40c0e40..93bd6c0 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 4834e06..f00eb7a 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 0000000..9d17355
--- /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 0000000..784e98b
--- /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 fa19bf4..bbe7825 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 0000000..6dc2a84
--- /dev/null
+++ b/redmine/app/views/issues/_history.rhtml
@@ -0,0 +1,11 @@
+<% for journal in journals %>
+
\ No newline at end of file
diff --git a/redmine/app/views/issues/show.rhtml b/redmine/app/views/issues/show.rhtml
index 43b7d04..8128b74 100644
--- a/redmine/app/views/issues/show.rhtml
+++ b/redmine/app/views/issues/show.rhtml
@@ -44,8 +44,8 @@ end %>
<%=l(:field_description)%> :