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 %> +
<%= @history.status.name %>
+<%= @new_status.name %>
- -
-<%= 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 6044fa9..60cdafc 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 0000000..2443cc7 --- /dev/null +++ b/redmine/app/views/issues/history.rhtml @@ -0,0 +1,6 @@ +<%= 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 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)%> :
+
+
<%= format_date(history.created_on) %> | -<%= history.author.display_name %> | -<%= history.status.name %> | -
<%= simple_format auto_link history.notes %> |
- <%= 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 %>