@@ -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 = |
|
|
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 |
|
|
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 |
|
|
|
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_ |
|
|
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 : |
|
|
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 |
|
|
|
65 | value.empty? ? "" : l_date(value.to_date) | |
|
61 | 66 | when "bool" |
|
62 |
l_YesNo( |
|
|
67 | l_YesNo(value == "1") | |
|
63 | 68 | else |
|
64 |
|
|
|
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_ |
|
|
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 |
|
|
73 | for journal in issue.journals.find(:all, :include => :user, :order => "journals.created_on desc") | |
|
73 | 74 |
|
|
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 |
|
|
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 |
|
|
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 ' |
|
|
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 ' |
|
|
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> <%= @ |
|
|
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=" |
|
|
31 |
<%= text_area ' |
|
|
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=" |
|
|
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 | |
|
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 | |
|
80 | 81 | <% end %> |
|
81 |
</ |
|
|
82 | </div> | |
|
83 | <div class="clear"></div> | |
|
82 | 84 | </div> |
|
83 | 85 | |
|
84 | <div class="box"> | |
|
85 |
<h3><%=l(:label_history)%> |
|
|
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=" |
|
|
134 |
<%= text_area ' |
|
|
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 |
|
|
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