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 e1e842b0..3bebf4de 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 45b4fe63..e196e822 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 a2b7abb4..c968800c 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 378545eb..0f0deeb3 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 38cb9df5..9df5c50a 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 4e7a173f..a0dab0f9 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 40c0e40a..93bd6c05 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 4834e069..f00eb7a9 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 00000000..9d173552 --- /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 00000000..784e98bf --- /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 fa19bf4d..bbe78252 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 00000000..6dc2a84b --- /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 %>

+ + <% 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 d12db0a2..1f6a1228 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 0fe07ad2..2ef87183 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 %>
-

<%= @history.status.name %>

+

<%= @new_status.name %>

<%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %> -

- -

-<%= text_area 'history', 'notes', :cols => 60, :rows => 10 %>

-
+

+

+<%= 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 6044fa9f..60cdafc1 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 00000000..2443cc73 --- /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 43b7d044..8128b74a 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]) %> - - - - - -<% if 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 %> -
<%= format_date(history.created_on) %><%= history.author.display_name %><%= history.status.name %>
<%= simple_format auto_link history.notes %>
@@ -130,9 +125,9 @@ end %>

<%= l(:label_add_note) %>

<%= start_form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) %> -

- <%= text_area 'history', 'notes', :cols => 60, :rows => 10 %>

+

+ <%= 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 e04d3cde..767f51f6 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 9942c244..00000000 --- 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 9942c244..00000000 --- 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 9942c244..00000000 --- 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 0534cc17..00000000 --- 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 e3612ea8..00000000 --- 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 e3612ea8..00000000 --- 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 e3612ea8..00000000 --- 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 831f301d..00000000 --- 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 00000000..1b97a571 --- /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 00000000..1b97a571 --- /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 00000000..1b97a571 --- /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 00000000..b7c3a50a --- /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 00000000..6170b5bd --- /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 f310f63c..bbf8a489 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 bc10248d..709ca18d 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 e30e6c7a..27cc0260 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 7c1c6e12..654efd73 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 5802558a..5973f1d3 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 c380b2c3..bd128a3a 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 5b780365..00000000 --- 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