##// END OF EJS Templates
improved issues change history...
Jean-Philippe Lang -
r52:42181112ff73
parent child
Show More
@@ -0,0 +1,22
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 class Journal < ActiveRecord::Base
19 belongs_to :journalized, :polymorphic => true
20 belongs_to :user
21 has_many :details, :class_name => "JournalDetail", :dependent => true
22 end
@@ -0,0 +1,20
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 class JournalDetail < ActiveRecord::Base
19 belongs_to :journal
20 end
@@ -0,0 +1,11
1 <% for journal in journals %>
2 <h4><%= format_time(journal.created_on) %> - <%= journal.user.name %></h4>
3 <ul>
4 <% for detail in journal.details %>
5 <li><%= show_detail(detail) %></li>
6 <% end %>
7 </ul>
8 <% if journal.notes? %>
9 <%= simple_format auto_link journal.notes %>
10 <% end %>
11 <% end %>
@@ -0,0 +1,6
1 <h3><%=l(:label_history)%></h3>
2 <div id="history">
3 <%= render :partial => 'history', :locals => { :journals => @journals } %>
4 </div>
5 <br />
6 <p><%= link_to l(:button_back), :action => 'show', :id => @issue %></p> No newline at end of file
@@ -0,0 +1,8
1 Issue #<%= @issue.id %> has been updated.
2 <%= @journal.user.name %>
3 <% for detail in @journal.details %>
4 <%= show_detail(detail) %>
5 <% end %>
6 <%= @journal.notes if @journal.notes? %>
7 ----------------------------------------
8 <%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %> No newline at end of file
@@ -0,0 +1,8
1 Issue #<%= @issue.id %> has been updated.
2 <%= @journal.user.name %>
3 <% for detail in @journal.details %>
4 <%= show_detail(detail) %>
5 <% end %>
6 <%= @journal.notes if @journal.notes? %>
7 ----------------------------------------
8 <%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %> No newline at end of file
@@ -0,0 +1,8
1 Issue #<%= @issue.id %> has been updated.
2 <%= @journal.user.name %>
3 <% for detail in @journal.details %>
4 <%= show_detail(detail) %>
5 <% end %>
6 <%= @journal.notes if @journal.notes? %>
7 ----------------------------------------
8 <%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %> No newline at end of file
@@ -0,0 +1,8
1 La demande #<%= @issue.id %> a été mise à jour.
2 <%= @journal.user.name %> - <%= format_date(@journal.created_on) %>
3 <% for detail in @journal.details %>
4 <%= show_detail(detail) %>
5 <% end %>
6 <%= journal.notes if journal.notes? %>
7 ----------------------------------------
8 <%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %> No newline at end of file
@@ -0,0 +1,54
1 class CreateJournals < ActiveRecord::Migration
2
3 # model removed, but needed for data migration
4 class IssueHistory < ActiveRecord::Base; belongs_to :issue; end
5
6 def self.up
7 create_table :journals, :force => true do |t|
8 t.column "journalized_id", :integer, :default => 0, :null => false
9 t.column "journalized_type", :string, :limit => 30, :default => "", :null => false
10 t.column "user_id", :integer, :default => 0, :null => false
11 t.column "notes", :text
12 t.column "created_on", :datetime, :null => false
13 end
14 create_table :journal_details, :force => true do |t|
15 t.column "journal_id", :integer, :default => 0, :null => false
16 t.column "property", :string, :limit => 30, :default => "", :null => false
17 t.column "prop_key", :string, :limit => 30, :default => "", :null => false
18 t.column "old_value", :string
19 t.column "value", :string
20 end
21
22 # indexes
23 add_index "journals", ["journalized_id", "journalized_type"], :name => "journals_journalized_id"
24 add_index "journal_details", ["journal_id"], :name => "journal_details_journal_id"
25
26 Permission.create :controller => "issues", :action => "history", :description => "label_history", :sort => 1006, :is_public => true, :mail_option => 0, :mail_enabled => 0
27
28 # data migration
29 IssueHistory.find(:all, :include => :issue).each {|h|
30 j = Journal.new(:journalized => h.issue, :user_id => h.author_id, :notes => h.notes, :created_on => h.created_on)
31 j.details << JournalDetail.new(:property => 'attr', :prop_key => 'status_id', :value => h.status_id)
32 j.save
33 }
34
35 drop_table :issue_histories
36 end
37
38 def self.down
39 drop_table :journal_details
40 drop_table :journals
41
42 create_table "issue_histories", :force => true do |t|
43 t.column "issue_id", :integer, :default => 0, :null => false
44 t.column "status_id", :integer, :default => 0, :null => false
45 t.column "author_id", :integer, :default => 0, :null => false
46 t.column "notes", :text, :default => ""
47 t.column "created_on", :timestamp
48 end
49
50 add_index "issue_histories", ["issue_id"], :name => "issue_histories_issue_id"
51
52 Permission.find(:first, :conditions => ["controller=? and action=?", 'issues', 'history']).destroy
53 end
54 end
@@ -41,7 +41,7 class ApplicationController < ActionController::Base
41 41 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
42 42 self.logged_in_user.language
43 43 elsif request.env['HTTP_ACCEPT_LANGUAGE']
44 accept_lang = HTTPUtils.parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
44 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
45 45 if accept_lang and !accept_lang.empty? and GLoc.valid_languages.include? accept_lang.to_sym
46 46 accept_lang
47 47 end
@@ -104,4 +104,23 class ApplicationController < ActionController::Base
104 104 session[:return_to] = nil
105 105 end
106 106 end
107
108 # qvalues http header parser
109 # code taken from webrick
110 def parse_qvalues(value)
111 tmp = []
112 if value
113 parts = value.split(/,\s*/)
114 parts.each {|part|
115 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
116 val = m[1]
117 q = (m[2] or 1).to_f
118 tmp.push([val, q])
119 end
120 }
121 tmp = tmp.sort_by{|val, q| -q}
122 tmp.collect!{|val, q| val}
123 end
124 return tmp
125 end
107 126 end No newline at end of file
@@ -27,6 +27,13 class IssuesController < ApplicationController
27 27 def show
28 28 @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
29 29 @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
30 @journals_count = @issue.journals.count
31 @journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "journals.created_on desc")
32 end
33
34 def history
35 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "journals.created_on desc")
36 @journals_count = @journals.length
30 37 end
31 38
32 39 def export_pdf
@@ -41,6 +48,7 class IssuesController < ApplicationController
41 48 @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) }
42 49 else
43 50 begin
51 @issue.init_journal(self.logged_in_user)
44 52 # Retrieve custom fields and values
45 53 @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]) }
46 54 @issue.custom_values = @custom_values
@@ -57,13 +65,14 class IssuesController < ApplicationController
57 65 end
58 66
59 67 def add_note
60 unless params[:history][:notes].empty?
61 @history = @issue.histories.build(params[:history])
62 @history.author_id = self.logged_in_user.id if self.logged_in_user
63 @history.status = @issue.status
64 if @history.save
68 unless params[:notes].empty?
69 journal = @issue.init_journal(self.logged_in_user, params[:notes])
70 #@history = @issue.histories.build(params[:history])
71 #@history.author_id = self.logged_in_user.id if self.logged_in_user
72 #@history.status = @issue.status
73 if @issue.save
65 74 flash[:notice] = l(:notice_successful_update)
66 Mailer.deliver_issue_add_note(@history) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
75 Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
67 76 redirect_to :action => 'show', :id => @issue
68 77 return
69 78 end
@@ -73,17 +82,20 class IssuesController < ApplicationController
73 82 end
74 83
75 84 def change_status
76 @history = @issue.histories.build(params[:history])
85 #@history = @issue.histories.build(params[:history])
77 86 @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
87 @new_status = IssueStatus.find(params[:new_status_id])
78 88 if params[:confirm]
79 89 begin
80 @history.author_id = self.logged_in_user.id if self.logged_in_user
81 @issue.status = @history.status
82 @issue.fixed_version_id = (params[:issue][:fixed_version_id])
83 @issue.assigned_to_id = (params[:issue][:assigned_to_id])
84 @issue.done_ratio = (params[:issue][:done_ratio])
85 @issue.lock_version = (params[:issue][:lock_version])
86 if @issue.save
90 #@history.author_id = self.logged_in_user.id if self.logged_in_user
91 #@issue.status = @history.status
92 #@issue.fixed_version_id = (params[:issue][:fixed_version_id])
93 #@issue.assigned_to_id = (params[:issue][:assigned_to_id])
94 #@issue.done_ratio = (params[:issue][:done_ratio])
95 #@issue.lock_version = (params[:issue][:lock_version])
96 @issue.init_journal(self.logged_in_user, params[:notes])
97 @issue.status = @new_status
98 if @issue.update_attributes(params[:issue])
87 99 flash[:notice] = l(:notice_successful_update)
88 100 Mailer.deliver_issue_change_status(@issue) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
89 101 redirect_to :action => 'show', :id => @issue
@@ -28,6 +28,7 class ProjectsController < ApplicationController
28 28 include CustomFieldsHelper
29 29 helper :ifpdf
30 30 include IfpdfHelper
31 helper IssuesHelper
31 32
32 33 def index
33 34 list
@@ -81,7 +81,7 module ApplicationHelper
81 81 end
82 82
83 83 def textilizable(text)
84 $RDM_TEXTILE_DISABLED ? text : textilize(text)
84 $RDM_TEXTILE_DISABLED ? text : RedCloth.new(text).to_html
85 85 end
86 86
87 87 def error_messages_for(object_name, options = {})
@@ -54,15 +54,20 module CustomFieldsHelper
54 54 # Return a string used to display a custom value
55 55 def show_value(custom_value)
56 56 return "" unless custom_value
57
58 case custom_value.custom_field.field_format
57 format_value(custom_value.value, custom_value.custom_field.field_format)
58 end
59
60 # Return a string used to display a custom value
61 def format_value(value, field_format)
62 return "" unless value
63 case field_format
59 64 when "date"
60 custom_value.value.empty? ? "" : l_date(custom_value.value.to_date)
65 value.empty? ? "" : l_date(value.to_date)
61 66 when "bool"
62 l_YesNo(custom_value.value == "1")
67 l_YesNo(value == "1")
63 68 else
64 custom_value.value
65 end
69 value
70 end
66 71 end
67 72
68 73 # Return an array of custom field formats which can be used in select_tag
@@ -25,12 +25,12 module IfpdfHelper
25 25
26 26 def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
27 27 @ic ||= Iconv.new('ISO-8859-1', 'UTF-8')
28 super w,h,@ic.iconv(txt),border,ln,align,fill,link
29 end
30
31 def MultiCell(w,h,txt,border=0,align='J',fill=0)
32 @ic ||= Iconv.new('ISO-8859-1', 'UTF-8')
33 super w,h,txt,border,align,fill
28 txt = begin
29 @ic.iconv(txt)
30 rescue
31 txt
32 end
33 super w,h,txt,border,ln,align,fill,link
34 34 end
35 35
36 36 def Footer
@@ -15,5 +15,60
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 module IssuesHelper
18 module IssuesHelper
19
20 def show_detail(detail, no_html=false)
21 case detail.property
22 when 'attr'
23 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
24 case detail.prop_key
25 when 'due_date', 'start_date'
26 value = format_date(detail.value.to_date) if detail.value
27 old_value = format_date(detail.old_value.to_date) if detail.old_value
28 when 'status_id'
29 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
30 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
31 when 'assigned_to_id'
32 u = User.find_by_id(detail.value) and value = u.name if detail.value
33 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
34 when 'priority_id'
35 e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
36 e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
37 when 'category_id'
38 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
39 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
40 when 'fixed_version_id'
41 v = Version.find_by_id(detail.value) and value = v.name if detail.value
42 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
43 end
44 when 'cf'
45 custom_field = CustomField.find_by_id(detail.prop_key)
46 if custom_field
47 label = custom_field.name
48 value = format_value(detail.value, custom_field.field_format) if detail.value
49 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
50 end
51 end
52
53 label ||= detail.prop_key
54 value ||= detail.value
55 old_value ||= detail.old_value
56
57 unless no_html
58 label = content_tag('strong', label)
59 old_value = content_tag("i", old_value) if old_value
60 old_value = content_tag("strike", old_value) if old_value and !value
61 value = content_tag("i", value) if value
62 end
63
64 if value
65 if old_value
66 label + " " + l(:text_journal_changed, old_value, value)
67 else
68 label + " " + l(:text_journal_set_to, value)
69 end
70 else
71 label + " " + l(:text_journal_deleted) + " (#{old_value})"
72 end
73 end
19 74 end
@@ -26,7 +26,8 class Issue < ActiveRecord::Base
26 26 belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
27 27 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
28 28
29 has_many :histories, :class_name => 'IssueHistory', :dependent => true, :order => "issue_histories.created_on DESC", :include => :status
29 #has_many :histories, :class_name => 'IssueHistory', :dependent => true, :order => "issue_histories.created_on DESC", :include => :status
30 has_many :journals, :as => :journalized, :dependent => true
30 31 has_many :attachments, :as => :container, :dependent => true
31 32
32 33 has_many :custom_values, :dependent => true, :as => :customized
@@ -51,8 +52,28 class Issue < ActiveRecord::Base
51 52 end
52 53 end
53 54
54 def before_create
55 build_history
55 #def before_create
56 # build_history
57 #end
58
59 def before_save
60 if @current_journal
61 # attributes changes
62 (Issue.column_names - %w(id description)).each {|c|
63 @current_journal.details << JournalDetail.new(:property => 'attr',
64 :prop_key => c,
65 :old_value => @issue_before_change.send(c),
66 :value => send(c)) unless send(c)==@issue_before_change.send(c)
67 }
68 # custom fields changes
69 custom_values.each {|c|
70 @current_journal.details << JournalDetail.new(:property => 'cf',
71 :prop_key => c.custom_field_id,
72 :old_value => @custom_values_before_change[c.custom_field_id],
73 :value => c.value) unless @custom_values_before_change[c.custom_field_id]==c.value
74 }
75 @current_journal.save unless @current_journal.details.empty? and @current_journal.notes.empty?
76 end
56 77 end
57 78
58 79 def long_id
@@ -63,12 +84,20 class Issue < ActiveRecord::Base
63 84 self.custom_values.each {|v| return v if v.custom_field_id == custom_field.id }
64 85 return nil
65 86 end
87
88 def init_journal(user, notes = "")
89 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
90 @issue_before_change = self.clone
91 @custom_values_before_change = {}
92 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
93 @current_journal
94 end
66 95
67 96 private
68 97 # Creates an history for the issue
69 def build_history
70 @history = self.histories.build
71 @history.status = self.status
72 @history.author = self.author
73 end
98 #def build_history
99 # @history = self.histories.build
100 # @history.status = self.status
101 # @history.author = self.author
102 #end
74 103 end
@@ -17,14 +17,6
17 17
18 18 class Mailer < ActionMailer::Base
19 19
20 def issue_change_status(issue)
21 # Sends to all project members
22 @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
23 @from = $RDM_MAIL_FROM
24 @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
25 @body['issue'] = issue
26 end
27
28 20 def issue_add(issue)
29 21 # Sends to all project members
30 22 @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
@@ -33,12 +25,14 class Mailer < ActionMailer::Base
33 25 @body['issue'] = issue
34 26 end
35 27
36 def issue_add_note(history)
28 def issue_edit(journal)
37 29 # Sends to all project members
38 @recipients = history.issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
30 issue = journal.journalized
31 @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
39 32 @from = $RDM_MAIL_FROM
40 @subject = "[#{history.issue.project.name} - #{history.issue.tracker.name} ##{history.issue.id}] #{history.issue.status.name} - #{history.issue.subject}"
41 @body['history'] = history
33 @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
34 @body['issue'] = issue
35 @body['journal']= journal
42 36 end
43 37
44 38 def lost_password(token)
@@ -66,29 +66,34
66 66 pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
67 67
68 68 pdf.Ln
69
69 70 pdf.SetFont('Arial','B',9)
70 pdf.Cell(190,5, l(:label_history),"B")
71 pdf.Ln
72 for history in issue.histories.find(:all, :include => [:author, :status])
73 pdf.SetFont('Arial','B',8)
74 pdf.Cell(100,5, history.status.name)
75 pdf.SetFont('Arial','',8)
76 pdf.Cell(20,5, format_date(history.created_on))
77 pdf.Cell(70,5, history.author.name,0,0,"R")
78 pdf.SetFont('Arial','',8)
71 pdf.Cell(190,5, l(:label_history), "B")
72 pdf.Ln
73 for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc")
74 pdf.SetFont('Arial','B',8)
75 pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
79 76 pdf.Ln
80 pdf.Cell(10,4, "") and pdf.MultiCell(180,4, history.notes) if history.notes?
77 pdf.SetFont('Arial','I',8)
78 for detail in journal.details
79 pdf.Cell(190,5, "- " + show_detail(detail, true))
80 pdf.Ln
81 end
82 if journal.notes?
83 pdf.SetFont('Arial','',8)
84 pdf.MultiCell(190,5, journal.notes)
85 end
86 pdf.Ln
81 87 end
82 pdf.Ln
83
88
84 89 pdf.SetFont('Arial','B',9)
85 90 pdf.Cell(190,5, l(:label_attachment_plural), "B")
86 pdf.Ln
91 pdf.Ln
87 92 for attachment in issue.attachments
88 93 pdf.SetFont('Arial','',8)
89 94 pdf.Cell(80,5, attachment.filename)
90 pdf.Cell(20,5, human_size(attachment.filesize))
91 pdf.Cell(20,5, format_date(attachment.created_on))
95 pdf.Cell(20,5, human_size(attachment.filesize),0,0,"R")
96 pdf.Cell(20,5, format_date(attachment.created_on),0,0,"R")
92 97 pdf.Cell(70,5, attachment.author.name,0,0,"R")
93 98 pdf.Ln
94 99 end
@@ -1,13 +1,13
1 1 <h2><%=l(:label_issue)%> #<%= @issue.id %>: <%= @issue.subject %></h2>
2 2
3 <%= error_messages_for 'history' %>
3 <%= error_messages_for 'issue' %>
4 4 <%= start_form_tag({:action => 'change_status', :id => @issue}, :class => "tabular") %>
5 5
6 6 <%= hidden_field_tag 'confirm', 1 %>
7 <%= hidden_field 'history', 'status_id' %>
7 <%= hidden_field_tag 'new_status_id', @new_status.id %>
8 8
9 9 <div class="box">
10 <p><label><%=l(:label_issue_status_new)%></label> <%= @history.status.name %></p>
10 <p><label><%=l(:label_issue_status_new)%></label> <%= @new_status.name %></p>
11 11
12 12 <p><label for="issue_assigned_to_id"><%=l(:field_assigned_to)%></label>
13 13 <select name="issue[assigned_to_id]">
@@ -25,12 +25,13
25 25 <select name="issue[fixed_version_id]">
26 26 <option value="">--none--</option>
27 27 <%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %>
28 </select></p>
29
30 <p><label for="history_notes"><%=l(:field_notes)%></label>
31 <%= text_area 'history', 'notes', :cols => 60, :rows => 10 %></p>
32 </div>
28 </select></p>
33 29
30 <p><label for="notes"><%= l(:field_notes) %></label>
31 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10 %></p>
32
33 </div>
34
34 35 <%= hidden_field 'issue', 'lock_version' %>
35 36 <%= submit_tag l(:button_save) %>
36 37 <%= end_form_tag %>
@@ -19,7 +19,7
19 19
20 20 <div class="clear">
21 21 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
22 <p><%= f.text_area :description, :cols => 60, :rows => 10, :required => true %></p>
22 <p><%= f.text_area :description, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :required => true %></p>
23 23
24 24 <% for @custom_value in @custom_values %>
25 25 <p><%= custom_field_tag_with_label @custom_value %></p>
@@ -44,8 +44,8 end %>
44 44
45 45 <b><%=l(:field_description)%> :</b><br /><br />
46 46 <%= textilizable @issue.description %>
47
48 <p>
47 <br />
48 <div style="float:left;">
49 49 <% if authorize_for('issues', 'edit') %>
50 50 <%= start_form_tag ({:controller => 'issues', :action => 'edit', :id => @issue}, :method => "get" ) %>
51 51 <%= submit_tag l(:button_edit) %>
@@ -56,7 +56,7 end %>
56 56 <% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %>
57 57 <%= start_form_tag ({:controller => 'issues', :action => 'change_status', :id => @issue}) %>
58 58 <%=l(:label_change_status)%> :
59 <select name="history[status_id]">
59 <select name="new_status_id">
60 60 <%= options_from_collection_for_select @status_options, "id", "name" %>
61 61 </select>
62 62 <%= submit_tag l(:button_change) %>
@@ -71,30 +71,25 end %>
71 71 <%= end_form_tag %>
72 72 &nbsp;&nbsp;
73 73 <% end %>
74
74 </div>
75 <div style="float:right;">
75 76 <% if authorize_for('issues', 'destroy') %>
76 77 <%= start_form_tag ({:controller => 'issues', :action => 'destroy', :id => @issue} ) %>
77 78 <%= submit_tag l(:button_delete) %>
78 79 <%= end_form_tag %>
79 80 &nbsp;&nbsp;
80 81 <% end %>
81 </p>
82 </div>
83 <div class="clear"></div>
82 84 </div>
83 85
84 <div class="box">
85 <h3><%=l(:label_history)%></h3>
86 <table width="100%">
87 <% for history in @issue.histories.find(:all, :include => [:author, :status]) %>
88 <tr>
89 <td><%= format_date(history.created_on) %></td>
90 <td><%= history.author.display_name %></td>
91 <td><b><%= history.status.name %></b></td>
92 </tr>
93 <% if history.notes? %>
94 <tr><td colspan=3><%= simple_format auto_link history.notes %></td></tr>
95 <% end %>
86 <div id="history" class="box">
87 <h3><%=l(:label_history)%>
88 <% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3>
89 <%= render :partial => 'history', :locals => { :journals => @journals } %>
90 <% if @journals_count > @journals.length %>
91 <p><center><small>[ <%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %> ]</small></center></p>
96 92 <% end %>
97 </table>
98 93 </div>
99 94
100 95 <div class="box">
@@ -130,9 +125,9 end %>
130 125 <div class="box">
131 126 <h3><%= l(:label_add_note) %></h3>
132 127 <%= start_form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) %>
133 <p><label for="history_notes"><%=l(:field_notes)%></label>
134 <%= text_area 'history', 'notes', :cols => 60, :rows => 10 %></p>
128 <p><label for="notes"><%=l(:field_notes)%></label>
129 <%= text_area_tag 'notes', '', :cols => 60, :rows => 10 %></p>
135 130 <%= submit_tag l(:button_add) %>
136 <%= end_form_tag %>
131 <%= end_form_tag %>
137 132 </div>
138 133 <% end %>
@@ -133,7 +133,7 var menu_contenu=' \
133 133 <div id="footer">
134 134 <p>
135 135 <%= auto_link $RDM_FOOTER_SIG %> |
136 <a href="http://redmine.org/" target="_new"><%= RDM_APP_NAME %></a> <%= RDM_APP_VERSION %>
136 <a href="http://redmine.rubyforge.org/" target="_new"><%= RDM_APP_NAME %></a> <%= RDM_APP_VERSION %>
137 137 </p>
138 138 </div>
139 139
@@ -7,6 +7,7 http://redmine.org/
7 7
8 8 == xx/xx/2006 v0.x.x
9 9
10 * improved issues change history
10 11 * new functionality: move an issue to another project or tracker
11 12 * new functionality: add a note to an issue
12 13 * new report: project activity
@@ -256,6 +256,8 label_calendar: Kalender
256 256 label_months_from: Monate von
257 257 label_gantt_chart: Gantt Diagramm
258 258 label_internal: Intern
259 label_last_changes: %d änderungen des Letzten
260 label_change_view_all: Alle änderungen ansehen
259 261
260 262 button_login: Einloggen
261 263 button_submit: Einreichen
@@ -285,6 +287,9 text_possible_values_info: Werte trennten sich mit |
285 287 text_project_destroy_confirmation: Sind sie sicher, daß sie das Projekt löschen wollen ?
286 288 text_workflow_edit: Auswahl Workflow zum Bearbeiten
287 289 text_are_you_sure: Sind sie sicher ?
290 text_journal_changed: geändert von %s zu %s
291 text_journal_set_to: gestellt zu %s
292 text_journal_deleted: gelöscht
288 293
289 294 default_role_manager: Manager
290 295 default_role_developper: Developer
@@ -256,6 +256,8 label_calendar: Calendar
256 256 label_months_from: months from
257 257 label_gantt_chart: Gantt chart
258 258 label_internal: Internal
259 label_last_changes: last %d changes
260 label_change_view_all: View all changes
259 261
260 262 button_login: Login
261 263 button_submit: Submit
@@ -285,6 +287,9 text_possible_values_info: values separated with |
285 287 text_project_destroy_confirmation: Are you sure you want to delete this project and all related data ?
286 288 text_workflow_edit: Select a role and a tracker to edit the workflow
287 289 text_are_you_sure: Are you sure ?
290 text_journal_changed: changed from %s to %s
291 text_journal_set_to: set to %s
292 text_journal_deleted: deleted
288 293
289 294 default_role_manager: Manager
290 295 default_role_developper: Developer
@@ -256,6 +256,8 label_calendar: Calendario
256 256 label_months_from: meses de
257 257 label_gantt_chart: Diagrama de Gantt
258 258 label_internal: Interno
259 label_last_changes: %d cambios del último
260 label_change_view_all: Ver todos los cambios
259 261
260 262 button_login: Conexión
261 263 button_submit: Someter
@@ -285,6 +287,9 text_possible_values_info: Los valores se separaron con |
285 287 text_project_destroy_confirmation: ¿ Estás seguro de querer eliminar el proyecto ?
286 288 text_workflow_edit: Seleccionar un workflow para actualizar
287 289 text_are_you_sure: ¿ Estás seguro ?
290 text_journal_changed: cambiado de %s a %s
291 text_journal_set_to: fijado a %s
292 text_journal_deleted: suprimido
288 293
289 294 default_role_manager: Manager
290 295 default_role_developper: Desarrollador
@@ -257,6 +257,8 label_calendar: Calendrier
257 257 label_months_from: mois depuis
258 258 label_gantt_chart: Diagramme de Gantt
259 259 label_internal: Interne
260 label_last_changes: %d derniers changements
261 label_change_view_all: Voir tous les changements
260 262
261 263 button_login: Connexion
262 264 button_submit: Soumettre
@@ -286,6 +288,9 text_possible_values_info: valeurs séparées par |
286 288 text_project_destroy_confirmation: Etes-vous sûr de vouloir supprimer ce projet et tout ce qui lui est rattaché ?
287 289 text_workflow_edit: Sélectionner un tracker et un rôle pour éditer le workflow
288 290 text_are_you_sure: Etes-vous sûr ?
291 text_journal_changed: changé de %s à %s
292 text_journal_set_to: mis à %s
293 text_journal_deleted: supprimé
289 294
290 295 default_role_manager: Manager
291 296 default_role_developper: Développeur
@@ -186,10 +186,6 form {
186 186
187 187 .noborder {
188 188 border:0px;
189 Exception exceptions.AssertionError: <exceptions.AssertionError instance at 0xb7c0b20c> in <bound
190 method SubversionRepository.__del__ of <vclib.svn.SubversionRepository instance at 0xb7c1252c>>
191 ignored
192
193 189 background-color:#fff;
194 190 width:100%;
195 191 }
@@ -292,7 +288,7 table.calenderTable td {
292 288 border:1px solid #578bb8;
293 289 }
294 290
295 hr { border:none; border-bottom: dotted 2px #c0c0c0; }
291 hr { border:none; border-bottom: dotted 1px #c0c0c0; }
296 292
297 293
298 294 /**************** Sidebar styles ****************/
@@ -409,6 +405,17 img.calendar-trigger {
409 405 margin-left: 4px;
410 406 }
411 407
408 #history h4 {
409 font-size: 1em;
410 margin-bottom: 12px;
411 margin-top: 20px;
412 font-weight: normal;
413 border-bottom: dotted 1px #c0c0c0;
414 }
415
416 #history p {
417 margin-left: 34px;
418 }
412 419
413 420 /***** CSS FORM ******/
414 421 .tabular p{
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now