##// 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,14 +54,19 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 format_value(custom_value.value, custom_value.custom_field.field_format)
58 end
57 59
58 case custom_value.custom_field.field_format
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
69 value
65 70 end
66 71 end
67 72
@@ -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
28 txt = begin
29 @ic.iconv(txt)
30 rescue
31 txt
29 32 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
33 super w,h,txt,border,ln,align,fill,link
34 34 end
35 35
36 36 def Footer
@@ -16,4 +16,59
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 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
@@ -64,11 +85,19 class Issue < ActiveRecord::Base
64 85 return nil
65 86 end
66 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
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,7 +17,7
17 17
18 18 class Mailer < ActionMailer::Base
19 19
20 def issue_change_status(issue)
20 def issue_add(issue)
21 21 # Sends to all project members
22 22 @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
23 23 @from = $RDM_MAIL_FROM
@@ -25,20 +25,14 class Mailer < ActionMailer::Base
25 25 @body['issue'] = issue
26 26 end
27 27
28 def issue_add(issue)
28 def issue_edit(journal)
29 29 # Sends to all project members
30 issue = journal.journalized
30 31 @recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
31 32 @from = $RDM_MAIL_FROM
32 33 @subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.status.name} - #{issue.subject}"
33 34 @body['issue'] = issue
34 end
35
36 def issue_add_note(history)
37 # Sends to all project members
38 @recipients = history.issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
39 @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
35 @body['journal']= journal
42 36 end
43 37
44 38 def lost_password(token)
@@ -66,20 +66,25
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 71 pdf.Cell(190,5, l(:label_history),"B")
71 72 pdf.Ln
72 for history in issue.histories.find(:all, :include => [:author, :status])
73 for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc")
73 74 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)
75 pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
76 pdf.Ln
77 pdf.SetFont('Arial','I',8)
78 for detail in journal.details
79 pdf.Cell(190,5, "- " + show_detail(detail, true))
79 80 pdf.Ln
80 pdf.Cell(10,4, "") and pdf.MultiCell(180,4, history.notes) if history.notes?
81 end
82 if journal.notes?
83 pdf.SetFont('Arial','',8)
84 pdf.MultiCell(190,5, journal.notes)
81 85 end
82 86 pdf.Ln
87 end
83 88
84 89 pdf.SetFont('Arial','B',9)
85 90 pdf.Cell(190,5, l(:label_attachment_plural), "B")
@@ -87,8 +92,8
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]">
@@ -27,8 +27,9
27 27 <%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %>
28 28 </select></p>
29 29
30 <p><label for="history_notes"><%=l(:field_notes)%></label>
31 <%= text_area 'history', 'notes', :cols => 60, :rows => 10 %></p>
30 <p><label for="notes"><%= l(:field_notes) %></label>
31 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10 %></p>
32
32 33 </div>
33 34
34 35 <%= hidden_field 'issue', 'lock_version' %>
@@ -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>
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>
95 92 <% end %>
96 <% end %>
97 </table>
98 93 </div>
99 94
100 95 <div class="box">
@@ -130,8 +125,8 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 131 <%= end_form_tag %>
137 132 </div>
@@ -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