##// END OF EJS Templates
Adds a Setting to control how an Issue's done_ratio is calculated:...
Eric Davis -
r3037:4fe14e71c2d7
parent child
Show More
@@ -0,0 +1,9
1 class AddDefaultDoneRatioToIssueStatus < ActiveRecord::Migration
2 def self.up
3 add_column :issue_statuses, :default_done_ratio, :integer
4 end
5
6 def self.down
7 remove_column :issue_statuses, :default_done_ratio
8 end
9 end
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,69 +1,78
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssueStatusesController < ApplicationController
18 class IssueStatusesController < ApplicationController
19 before_filter :require_admin
19 before_filter :require_admin
20
20
21 verify :method => :post, :only => [ :destroy, :create, :update, :move ],
21 verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
22 :redirect_to => { :action => :list }
22 :redirect_to => { :action => :list }
23
23
24 def index
24 def index
25 list
25 list
26 render :action => 'list' unless request.xhr?
26 render :action => 'list' unless request.xhr?
27 end
27 end
28
28
29 def list
29 def list
30 @issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
30 @issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
31 render :action => "list", :layout => false if request.xhr?
31 render :action => "list", :layout => false if request.xhr?
32 end
32 end
33
33
34 def new
34 def new
35 @issue_status = IssueStatus.new
35 @issue_status = IssueStatus.new
36 end
36 end
37
37
38 def create
38 def create
39 @issue_status = IssueStatus.new(params[:issue_status])
39 @issue_status = IssueStatus.new(params[:issue_status])
40 if @issue_status.save
40 if @issue_status.save
41 flash[:notice] = l(:notice_successful_create)
41 flash[:notice] = l(:notice_successful_create)
42 redirect_to :action => 'list'
42 redirect_to :action => 'list'
43 else
43 else
44 render :action => 'new'
44 render :action => 'new'
45 end
45 end
46 end
46 end
47
47
48 def edit
48 def edit
49 @issue_status = IssueStatus.find(params[:id])
49 @issue_status = IssueStatus.find(params[:id])
50 end
50 end
51
51
52 def update
52 def update
53 @issue_status = IssueStatus.find(params[:id])
53 @issue_status = IssueStatus.find(params[:id])
54 if @issue_status.update_attributes(params[:issue_status])
54 if @issue_status.update_attributes(params[:issue_status])
55 flash[:notice] = l(:notice_successful_update)
55 flash[:notice] = l(:notice_successful_update)
56 redirect_to :action => 'list'
56 redirect_to :action => 'list'
57 else
57 else
58 render :action => 'edit'
58 render :action => 'edit'
59 end
59 end
60 end
60 end
61
61
62 def destroy
62 def destroy
63 IssueStatus.find(params[:id]).destroy
63 IssueStatus.find(params[:id]).destroy
64 redirect_to :action => 'list'
64 redirect_to :action => 'list'
65 rescue
65 rescue
66 flash[:error] = "Unable to delete issue status"
66 flash[:error] = "Unable to delete issue status"
67 redirect_to :action => 'list'
67 redirect_to :action => 'list'
68 end
68 end
69
70 def update_issue_done_ratio
71 if IssueStatus.update_issue_done_ratios
72 flash[:notice] = l(:notice_issue_done_ratios_updated)
73 else
74 flash[:error] = l(:error_issue_done_ratios_not_updated)
75 end
76 redirect_to :action => 'list'
77 end
69 end
78 end
@@ -1,425 +1,452
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Issue < ActiveRecord::Base
18 class Issue < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 belongs_to :tracker
20 belongs_to :tracker
21 belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
21 belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
22 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
22 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
23 belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
24 belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
24 belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
25 belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id'
25 belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id'
26 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
26 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
27
27
28 has_many :journals, :as => :journalized, :dependent => :destroy
28 has_many :journals, :as => :journalized, :dependent => :destroy
29 has_many :time_entries, :dependent => :delete_all
29 has_many :time_entries, :dependent => :delete_all
30 has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
30 has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
31
31
32 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
32 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
33 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
33 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
34
34
35 acts_as_attachable :after_remove => :attachment_removed
35 acts_as_attachable :after_remove => :attachment_removed
36 acts_as_customizable
36 acts_as_customizable
37 acts_as_watchable
37 acts_as_watchable
38 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
38 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
39 :include => [:project, :journals],
39 :include => [:project, :journals],
40 # sort by id so that limited eager loading doesn't break with postgresql
40 # sort by id so that limited eager loading doesn't break with postgresql
41 :order_column => "#{table_name}.id"
41 :order_column => "#{table_name}.id"
42 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
42 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
43 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
43 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
44 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
44 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
45
45
46 acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
46 acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
47 :author_key => :author_id
47 :author_key => :author_id
48
49 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
48
50
49 validates_presence_of :subject, :priority, :project, :tracker, :author, :status
51 validates_presence_of :subject, :priority, :project, :tracker, :author, :status
50 validates_length_of :subject, :maximum => 255
52 validates_length_of :subject, :maximum => 255
51 validates_inclusion_of :done_ratio, :in => 0..100
53 validates_inclusion_of :done_ratio, :in => 0..100
52 validates_numericality_of :estimated_hours, :allow_nil => true
54 validates_numericality_of :estimated_hours, :allow_nil => true
53
55
54 named_scope :visible, lambda {|*args| { :include => :project,
56 named_scope :visible, lambda {|*args| { :include => :project,
55 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
57 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
56
58
57 named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
59 named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
58
60
61 before_save :update_done_ratio_from_issue_status
59 after_save :create_journal
62 after_save :create_journal
60
63
61 # Returns true if usr or current user is allowed to view the issue
64 # Returns true if usr or current user is allowed to view the issue
62 def visible?(usr=nil)
65 def visible?(usr=nil)
63 (usr || User.current).allowed_to?(:view_issues, self.project)
66 (usr || User.current).allowed_to?(:view_issues, self.project)
64 end
67 end
65
68
66 def after_initialize
69 def after_initialize
67 if new_record?
70 if new_record?
68 # set default values for new records only
71 # set default values for new records only
69 self.status ||= IssueStatus.default
72 self.status ||= IssueStatus.default
70 self.priority ||= IssuePriority.default
73 self.priority ||= IssuePriority.default
71 end
74 end
72 end
75 end
73
76
74 # Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
77 # Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
75 def available_custom_fields
78 def available_custom_fields
76 (project && tracker) ? project.all_issue_custom_fields.select {|c| tracker.custom_fields.include? c } : []
79 (project && tracker) ? project.all_issue_custom_fields.select {|c| tracker.custom_fields.include? c } : []
77 end
80 end
78
81
79 def copy_from(arg)
82 def copy_from(arg)
80 issue = arg.is_a?(Issue) ? arg : Issue.find(arg)
83 issue = arg.is_a?(Issue) ? arg : Issue.find(arg)
81 self.attributes = issue.attributes.dup.except("id", "created_on", "updated_on")
84 self.attributes = issue.attributes.dup.except("id", "created_on", "updated_on")
82 self.custom_values = issue.custom_values.collect {|v| v.clone}
85 self.custom_values = issue.custom_values.collect {|v| v.clone}
83 self.status = issue.status
86 self.status = issue.status
84 self
87 self
85 end
88 end
86
89
87 # Moves/copies an issue to a new project and tracker
90 # Moves/copies an issue to a new project and tracker
88 # Returns the moved/copied issue on success, false on failure
91 # Returns the moved/copied issue on success, false on failure
89 def move_to(new_project, new_tracker = nil, options = {})
92 def move_to(new_project, new_tracker = nil, options = {})
90 options ||= {}
93 options ||= {}
91 issue = options[:copy] ? self.clone : self
94 issue = options[:copy] ? self.clone : self
92 transaction do
95 transaction do
93 if new_project && issue.project_id != new_project.id
96 if new_project && issue.project_id != new_project.id
94 # delete issue relations
97 # delete issue relations
95 unless Setting.cross_project_issue_relations?
98 unless Setting.cross_project_issue_relations?
96 issue.relations_from.clear
99 issue.relations_from.clear
97 issue.relations_to.clear
100 issue.relations_to.clear
98 end
101 end
99 # issue is moved to another project
102 # issue is moved to another project
100 # reassign to the category with same name if any
103 # reassign to the category with same name if any
101 new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
104 new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
102 issue.category = new_category
105 issue.category = new_category
103 # Keep the fixed_version if it's still valid in the new_project
106 # Keep the fixed_version if it's still valid in the new_project
104 unless new_project.shared_versions.include?(issue.fixed_version)
107 unless new_project.shared_versions.include?(issue.fixed_version)
105 issue.fixed_version = nil
108 issue.fixed_version = nil
106 end
109 end
107 issue.project = new_project
110 issue.project = new_project
108 end
111 end
109 if new_tracker
112 if new_tracker
110 issue.tracker = new_tracker
113 issue.tracker = new_tracker
111 end
114 end
112 if options[:copy]
115 if options[:copy]
113 issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
116 issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
114 issue.status = if options[:attributes] && options[:attributes][:status_id]
117 issue.status = if options[:attributes] && options[:attributes][:status_id]
115 IssueStatus.find_by_id(options[:attributes][:status_id])
118 IssueStatus.find_by_id(options[:attributes][:status_id])
116 else
119 else
117 self.status
120 self.status
118 end
121 end
119 end
122 end
120 # Allow bulk setting of attributes on the issue
123 # Allow bulk setting of attributes on the issue
121 if options[:attributes]
124 if options[:attributes]
122 issue.attributes = options[:attributes]
125 issue.attributes = options[:attributes]
123 end
126 end
124 if issue.save
127 if issue.save
125 unless options[:copy]
128 unless options[:copy]
126 # Manually update project_id on related time entries
129 # Manually update project_id on related time entries
127 TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
130 TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
128 end
131 end
129 else
132 else
130 Issue.connection.rollback_db_transaction
133 Issue.connection.rollback_db_transaction
131 return false
134 return false
132 end
135 end
133 end
136 end
134 return issue
137 return issue
135 end
138 end
136
139
137 def priority_id=(pid)
140 def priority_id=(pid)
138 self.priority = nil
141 self.priority = nil
139 write_attribute(:priority_id, pid)
142 write_attribute(:priority_id, pid)
140 end
143 end
141
144
142 def tracker_id=(tid)
145 def tracker_id=(tid)
143 self.tracker = nil
146 self.tracker = nil
144 write_attribute(:tracker_id, tid)
147 write_attribute(:tracker_id, tid)
145 result = write_attribute(:tracker_id, tid)
148 result = write_attribute(:tracker_id, tid)
146 @custom_field_values = nil
149 @custom_field_values = nil
147 result
150 result
148 end
151 end
149
152
150 # Overrides attributes= so that tracker_id gets assigned first
153 # Overrides attributes= so that tracker_id gets assigned first
151 def attributes_with_tracker_first=(new_attributes, *args)
154 def attributes_with_tracker_first=(new_attributes, *args)
152 return if new_attributes.nil?
155 return if new_attributes.nil?
153 new_tracker_id = new_attributes['tracker_id'] || new_attributes[:tracker_id]
156 new_tracker_id = new_attributes['tracker_id'] || new_attributes[:tracker_id]
154 if new_tracker_id
157 if new_tracker_id
155 self.tracker_id = new_tracker_id
158 self.tracker_id = new_tracker_id
156 end
159 end
157 self.attributes_without_tracker_first = new_attributes, *args
160 self.attributes_without_tracker_first = new_attributes, *args
158 end
161 end
159 alias_method_chain :attributes=, :tracker_first
162 alias_method_chain :attributes=, :tracker_first
160
163
161 def estimated_hours=(h)
164 def estimated_hours=(h)
162 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
165 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
163 end
166 end
164
167
168 def done_ratio
169 if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank?
170 self.status.default_done_ratio
171 else
172 read_attribute(:done_ratio)
173 end
174 end
175
176 def self.use_status_for_done_ratio?
177 Setting.issue_done_ratio == 'issue_status'
178 end
179
180 def self.use_field_for_done_ratio?
181 Setting.issue_done_ratio == 'issue_field'
182 end
183
165 def validate
184 def validate
166 if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
185 if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
167 errors.add :due_date, :not_a_date
186 errors.add :due_date, :not_a_date
168 end
187 end
169
188
170 if self.due_date and self.start_date and self.due_date < self.start_date
189 if self.due_date and self.start_date and self.due_date < self.start_date
171 errors.add :due_date, :greater_than_start_date
190 errors.add :due_date, :greater_than_start_date
172 end
191 end
173
192
174 if start_date && soonest_start && start_date < soonest_start
193 if start_date && soonest_start && start_date < soonest_start
175 errors.add :start_date, :invalid
194 errors.add :start_date, :invalid
176 end
195 end
177
196
178 if fixed_version
197 if fixed_version
179 if !assignable_versions.include?(fixed_version)
198 if !assignable_versions.include?(fixed_version)
180 errors.add :fixed_version_id, :inclusion
199 errors.add :fixed_version_id, :inclusion
181 elsif reopened? && fixed_version.closed?
200 elsif reopened? && fixed_version.closed?
182 errors.add_to_base I18n.t(:error_can_not_reopen_issue_on_closed_version)
201 errors.add_to_base I18n.t(:error_can_not_reopen_issue_on_closed_version)
183 end
202 end
184 end
203 end
185
204
186 # Checks that the issue can not be added/moved to a disabled tracker
205 # Checks that the issue can not be added/moved to a disabled tracker
187 if project && (tracker_id_changed? || project_id_changed?)
206 if project && (tracker_id_changed? || project_id_changed?)
188 unless project.trackers.include?(tracker)
207 unless project.trackers.include?(tracker)
189 errors.add :tracker_id, :inclusion
208 errors.add :tracker_id, :inclusion
190 end
209 end
191 end
210 end
192 end
211 end
193
212
194 def before_create
213 def before_create
195 # default assignment based on category
214 # default assignment based on category
196 if assigned_to.nil? && category && category.assigned_to
215 if assigned_to.nil? && category && category.assigned_to
197 self.assigned_to = category.assigned_to
216 self.assigned_to = category.assigned_to
198 end
217 end
199 end
218 end
200
219
220 # Set the done_ratio using the status if that setting is set. This will keep the done_ratios
221 # even if the user turns off the setting later
222 def update_done_ratio_from_issue_status
223 if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank?
224 self.done_ratio = self.status.default_done_ratio
225 end
226 end
227
201 def after_save
228 def after_save
202 # Reload is needed in order to get the right status
229 # Reload is needed in order to get the right status
203 reload
230 reload
204
231
205 # Update start/due dates of following issues
232 # Update start/due dates of following issues
206 relations_from.each(&:set_issue_to_dates)
233 relations_from.each(&:set_issue_to_dates)
207
234
208 # Close duplicates if the issue was closed
235 # Close duplicates if the issue was closed
209 if @issue_before_change && !@issue_before_change.closed? && self.closed?
236 if @issue_before_change && !@issue_before_change.closed? && self.closed?
210 duplicates.each do |duplicate|
237 duplicates.each do |duplicate|
211 # Reload is need in case the duplicate was updated by a previous duplicate
238 # Reload is need in case the duplicate was updated by a previous duplicate
212 duplicate.reload
239 duplicate.reload
213 # Don't re-close it if it's already closed
240 # Don't re-close it if it's already closed
214 next if duplicate.closed?
241 next if duplicate.closed?
215 # Same user and notes
242 # Same user and notes
216 duplicate.init_journal(@current_journal.user, @current_journal.notes)
243 duplicate.init_journal(@current_journal.user, @current_journal.notes)
217 duplicate.update_attribute :status, self.status
244 duplicate.update_attribute :status, self.status
218 end
245 end
219 end
246 end
220 end
247 end
221
248
222 def init_journal(user, notes = "")
249 def init_journal(user, notes = "")
223 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
250 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
224 @issue_before_change = self.clone
251 @issue_before_change = self.clone
225 @issue_before_change.status = self.status
252 @issue_before_change.status = self.status
226 @custom_values_before_change = {}
253 @custom_values_before_change = {}
227 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
254 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
228 # Make sure updated_on is updated when adding a note.
255 # Make sure updated_on is updated when adding a note.
229 updated_on_will_change!
256 updated_on_will_change!
230 @current_journal
257 @current_journal
231 end
258 end
232
259
233 # Return true if the issue is closed, otherwise false
260 # Return true if the issue is closed, otherwise false
234 def closed?
261 def closed?
235 self.status.is_closed?
262 self.status.is_closed?
236 end
263 end
237
264
238 # Return true if the issue is being reopened
265 # Return true if the issue is being reopened
239 def reopened?
266 def reopened?
240 if !new_record? && status_id_changed?
267 if !new_record? && status_id_changed?
241 status_was = IssueStatus.find_by_id(status_id_was)
268 status_was = IssueStatus.find_by_id(status_id_was)
242 status_new = IssueStatus.find_by_id(status_id)
269 status_new = IssueStatus.find_by_id(status_id)
243 if status_was && status_new && status_was.is_closed? && !status_new.is_closed?
270 if status_was && status_new && status_was.is_closed? && !status_new.is_closed?
244 return true
271 return true
245 end
272 end
246 end
273 end
247 false
274 false
248 end
275 end
249
276
250 # Returns true if the issue is overdue
277 # Returns true if the issue is overdue
251 def overdue?
278 def overdue?
252 !due_date.nil? && (due_date < Date.today) && !status.is_closed?
279 !due_date.nil? && (due_date < Date.today) && !status.is_closed?
253 end
280 end
254
281
255 # Users the issue can be assigned to
282 # Users the issue can be assigned to
256 def assignable_users
283 def assignable_users
257 project.assignable_users
284 project.assignable_users
258 end
285 end
259
286
260 # Versions that the issue can be assigned to
287 # Versions that the issue can be assigned to
261 def assignable_versions
288 def assignable_versions
262 @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
289 @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
263 end
290 end
264
291
265 # Returns true if this issue is blocked by another issue that is still open
292 # Returns true if this issue is blocked by another issue that is still open
266 def blocked?
293 def blocked?
267 !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil?
294 !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil?
268 end
295 end
269
296
270 # Returns an array of status that user is able to apply
297 # Returns an array of status that user is able to apply
271 def new_statuses_allowed_to(user)
298 def new_statuses_allowed_to(user)
272 statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker)
299 statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker)
273 statuses << status unless statuses.empty?
300 statuses << status unless statuses.empty?
274 statuses = statuses.uniq.sort
301 statuses = statuses.uniq.sort
275 blocked? ? statuses.reject {|s| s.is_closed?} : statuses
302 blocked? ? statuses.reject {|s| s.is_closed?} : statuses
276 end
303 end
277
304
278 # Returns the mail adresses of users that should be notified
305 # Returns the mail adresses of users that should be notified
279 def recipients
306 def recipients
280 notified = project.notified_users
307 notified = project.notified_users
281 # Author and assignee are always notified unless they have been locked
308 # Author and assignee are always notified unless they have been locked
282 notified << author if author && author.active?
309 notified << author if author && author.active?
283 notified << assigned_to if assigned_to && assigned_to.active?
310 notified << assigned_to if assigned_to && assigned_to.active?
284 notified.uniq!
311 notified.uniq!
285 # Remove users that can not view the issue
312 # Remove users that can not view the issue
286 notified.reject! {|user| !visible?(user)}
313 notified.reject! {|user| !visible?(user)}
287 notified.collect(&:mail)
314 notified.collect(&:mail)
288 end
315 end
289
316
290 # Returns the mail adresses of watchers that should be notified
317 # Returns the mail adresses of watchers that should be notified
291 def watcher_recipients
318 def watcher_recipients
292 notified = watcher_users
319 notified = watcher_users
293 notified.reject! {|user| !user.active? || !visible?(user)}
320 notified.reject! {|user| !user.active? || !visible?(user)}
294 notified.collect(&:mail)
321 notified.collect(&:mail)
295 end
322 end
296
323
297 # Returns the total number of hours spent on this issue.
324 # Returns the total number of hours spent on this issue.
298 #
325 #
299 # Example:
326 # Example:
300 # spent_hours => 0
327 # spent_hours => 0
301 # spent_hours => 50
328 # spent_hours => 50
302 def spent_hours
329 def spent_hours
303 @spent_hours ||= time_entries.sum(:hours) || 0
330 @spent_hours ||= time_entries.sum(:hours) || 0
304 end
331 end
305
332
306 def relations
333 def relations
307 (relations_from + relations_to).sort
334 (relations_from + relations_to).sort
308 end
335 end
309
336
310 def all_dependent_issues
337 def all_dependent_issues
311 dependencies = []
338 dependencies = []
312 relations_from.each do |relation|
339 relations_from.each do |relation|
313 dependencies << relation.issue_to
340 dependencies << relation.issue_to
314 dependencies += relation.issue_to.all_dependent_issues
341 dependencies += relation.issue_to.all_dependent_issues
315 end
342 end
316 dependencies
343 dependencies
317 end
344 end
318
345
319 # Returns an array of issues that duplicate this one
346 # Returns an array of issues that duplicate this one
320 def duplicates
347 def duplicates
321 relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from}
348 relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from}
322 end
349 end
323
350
324 # Returns the due date or the target due date if any
351 # Returns the due date or the target due date if any
325 # Used on gantt chart
352 # Used on gantt chart
326 def due_before
353 def due_before
327 due_date || (fixed_version ? fixed_version.effective_date : nil)
354 due_date || (fixed_version ? fixed_version.effective_date : nil)
328 end
355 end
329
356
330 # Returns the time scheduled for this issue.
357 # Returns the time scheduled for this issue.
331 #
358 #
332 # Example:
359 # Example:
333 # Start Date: 2/26/09, End Date: 3/04/09
360 # Start Date: 2/26/09, End Date: 3/04/09
334 # duration => 6
361 # duration => 6
335 def duration
362 def duration
336 (start_date && due_date) ? due_date - start_date : 0
363 (start_date && due_date) ? due_date - start_date : 0
337 end
364 end
338
365
339 def soonest_start
366 def soonest_start
340 @soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
367 @soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
341 end
368 end
342
369
343 def to_s
370 def to_s
344 "#{tracker} ##{id}: #{subject}"
371 "#{tracker} ##{id}: #{subject}"
345 end
372 end
346
373
347 # Returns a string of css classes that apply to the issue
374 # Returns a string of css classes that apply to the issue
348 def css_classes
375 def css_classes
349 s = "issue status-#{status.position} priority-#{priority.position}"
376 s = "issue status-#{status.position} priority-#{priority.position}"
350 s << ' closed' if closed?
377 s << ' closed' if closed?
351 s << ' overdue' if overdue?
378 s << ' overdue' if overdue?
352 s << ' created-by-me' if User.current.logged? && author_id == User.current.id
379 s << ' created-by-me' if User.current.logged? && author_id == User.current.id
353 s << ' assigned-to-me' if User.current.logged? && assigned_to_id == User.current.id
380 s << ' assigned-to-me' if User.current.logged? && assigned_to_id == User.current.id
354 s
381 s
355 end
382 end
356
383
357 # Unassigns issues from +version+ if it's no longer shared with issue's project
384 # Unassigns issues from +version+ if it's no longer shared with issue's project
358 def self.update_versions_from_sharing_change(version)
385 def self.update_versions_from_sharing_change(version)
359 # Update issues assigned to the version
386 # Update issues assigned to the version
360 update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
387 update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
361 end
388 end
362
389
363 # Unassigns issues from versions that are no longer shared
390 # Unassigns issues from versions that are no longer shared
364 # after +project+ was moved
391 # after +project+ was moved
365 def self.update_versions_from_hierarchy_change(project)
392 def self.update_versions_from_hierarchy_change(project)
366 moved_project_ids = project.self_and_descendants.reload.collect(&:id)
393 moved_project_ids = project.self_and_descendants.reload.collect(&:id)
367 # Update issues of the moved projects and issues assigned to a version of a moved project
394 # Update issues of the moved projects and issues assigned to a version of a moved project
368 Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
395 Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
369 end
396 end
370
397
371 private
398 private
372
399
373 # Update issues so their versions are not pointing to a
400 # Update issues so their versions are not pointing to a
374 # fixed_version that is not shared with the issue's project
401 # fixed_version that is not shared with the issue's project
375 def self.update_versions(conditions=nil)
402 def self.update_versions(conditions=nil)
376 # Only need to update issues with a fixed_version from
403 # Only need to update issues with a fixed_version from
377 # a different project and that is not systemwide shared
404 # a different project and that is not systemwide shared
378 Issue.all(:conditions => merge_conditions("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
405 Issue.all(:conditions => merge_conditions("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
379 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
406 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
380 " AND #{Version.table_name}.sharing <> 'system'",
407 " AND #{Version.table_name}.sharing <> 'system'",
381 conditions),
408 conditions),
382 :include => [:project, :fixed_version]
409 :include => [:project, :fixed_version]
383 ).each do |issue|
410 ).each do |issue|
384 next if issue.project.nil? || issue.fixed_version.nil?
411 next if issue.project.nil? || issue.fixed_version.nil?
385 unless issue.project.shared_versions.include?(issue.fixed_version)
412 unless issue.project.shared_versions.include?(issue.fixed_version)
386 issue.init_journal(User.current)
413 issue.init_journal(User.current)
387 issue.fixed_version = nil
414 issue.fixed_version = nil
388 issue.save
415 issue.save
389 end
416 end
390 end
417 end
391 end
418 end
392
419
393 # Callback on attachment deletion
420 # Callback on attachment deletion
394 def attachment_removed(obj)
421 def attachment_removed(obj)
395 journal = init_journal(User.current)
422 journal = init_journal(User.current)
396 journal.details << JournalDetail.new(:property => 'attachment',
423 journal.details << JournalDetail.new(:property => 'attachment',
397 :prop_key => obj.id,
424 :prop_key => obj.id,
398 :old_value => obj.filename)
425 :old_value => obj.filename)
399 journal.save
426 journal.save
400 end
427 end
401
428
402 # Saves the changes in a Journal
429 # Saves the changes in a Journal
403 # Called after_save
430 # Called after_save
404 def create_journal
431 def create_journal
405 if @current_journal
432 if @current_journal
406 # attributes changes
433 # attributes changes
407 (Issue.column_names - %w(id description lock_version created_on updated_on)).each {|c|
434 (Issue.column_names - %w(id description lock_version created_on updated_on)).each {|c|
408 @current_journal.details << JournalDetail.new(:property => 'attr',
435 @current_journal.details << JournalDetail.new(:property => 'attr',
409 :prop_key => c,
436 :prop_key => c,
410 :old_value => @issue_before_change.send(c),
437 :old_value => @issue_before_change.send(c),
411 :value => send(c)) unless send(c)==@issue_before_change.send(c)
438 :value => send(c)) unless send(c)==@issue_before_change.send(c)
412 }
439 }
413 # custom fields changes
440 # custom fields changes
414 custom_values.each {|c|
441 custom_values.each {|c|
415 next if (@custom_values_before_change[c.custom_field_id]==c.value ||
442 next if (@custom_values_before_change[c.custom_field_id]==c.value ||
416 (@custom_values_before_change[c.custom_field_id].blank? && c.value.blank?))
443 (@custom_values_before_change[c.custom_field_id].blank? && c.value.blank?))
417 @current_journal.details << JournalDetail.new(:property => 'cf',
444 @current_journal.details << JournalDetail.new(:property => 'cf',
418 :prop_key => c.custom_field_id,
445 :prop_key => c.custom_field_id,
419 :old_value => @custom_values_before_change[c.custom_field_id],
446 :old_value => @custom_values_before_change[c.custom_field_id],
420 :value => c.value)
447 :value => c.value)
421 }
448 }
422 @current_journal.save
449 @current_journal.save
423 end
450 end
424 end
451 end
425 end
452 end
@@ -1,79 +1,91
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssueStatus < ActiveRecord::Base
18 class IssueStatus < ActiveRecord::Base
19 before_destroy :check_integrity
19 before_destroy :check_integrity
20 has_many :workflows, :foreign_key => "old_status_id", :dependent => :delete_all
20 has_many :workflows, :foreign_key => "old_status_id", :dependent => :delete_all
21 acts_as_list
21 acts_as_list
22
22
23 validates_presence_of :name
23 validates_presence_of :name
24 validates_uniqueness_of :name
24 validates_uniqueness_of :name
25 validates_length_of :name, :maximum => 30
25 validates_length_of :name, :maximum => 30
26 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
26 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
27
27
28 def after_save
28 def after_save
29 IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
29 IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
30 end
30 end
31
31
32 # Returns the default status for new issues
32 # Returns the default status for new issues
33 def self.default
33 def self.default
34 find(:first, :conditions =>["is_default=?", true])
34 find(:first, :conditions =>["is_default=?", true])
35 end
35 end
36
37 # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
38 def self.update_issue_done_ratios
39 if Issue.use_status_for_done_ratio?
40 IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status|
41 Issue.update_all(["done_ratio = ?", status.default_done_ratio],
42 ["status_id = ?", status.id])
43 end
44 end
45
46 return Issue.use_status_for_done_ratio?
47 end
36
48
37 # Returns an array of all statuses the given role can switch to
49 # Returns an array of all statuses the given role can switch to
38 # Uses association cache when called more than one time
50 # Uses association cache when called more than one time
39 def new_statuses_allowed_to(roles, tracker)
51 def new_statuses_allowed_to(roles, tracker)
40 if roles && tracker
52 if roles && tracker
41 role_ids = roles.collect(&:id)
53 role_ids = roles.collect(&:id)
42 new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
54 new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
43 else
55 else
44 []
56 []
45 end
57 end
46 end
58 end
47
59
48 # Same thing as above but uses a database query
60 # Same thing as above but uses a database query
49 # More efficient than the previous method if called just once
61 # More efficient than the previous method if called just once
50 def find_new_statuses_allowed_to(roles, tracker)
62 def find_new_statuses_allowed_to(roles, tracker)
51 if roles && tracker
63 if roles && tracker
52 workflows.find(:all,
64 workflows.find(:all,
53 :include => :new_status,
65 :include => :new_status,
54 :conditions => { :role_id => roles.collect(&:id),
66 :conditions => { :role_id => roles.collect(&:id),
55 :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
67 :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
56 else
68 else
57 []
69 []
58 end
70 end
59 end
71 end
60
72
61 def new_status_allowed_to?(status, roles, tracker)
73 def new_status_allowed_to?(status, roles, tracker)
62 if status && roles && tracker
74 if status && roles && tracker
63 !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
75 !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
64 else
76 else
65 false
77 false
66 end
78 end
67 end
79 end
68
80
69 def <=>(status)
81 def <=>(status)
70 position <=> status.position
82 position <=> status.position
71 end
83 end
72
84
73 def to_s; name end
85 def to_s; name end
74
86
75 private
87 private
76 def check_integrity
88 def check_integrity
77 raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
89 raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
78 end
90 end
79 end
91 end
@@ -1,17 +1,22
1 <%= error_messages_for 'issue_status' %>
1 <%= error_messages_for 'issue_status' %>
2
2
3 <div class="box">
3 <div class="box">
4 <!--[form:issue_status]-->
4 <!--[form:issue_status]-->
5 <p><label for="issue_status_name"><%=l(:field_name)%><span class="required"> *</span></label>
5 <p><label for="issue_status_name"><%=l(:field_name)%><span class="required"> *</span></label>
6 <%= text_field 'issue_status', 'name' %></p>
6 <%= text_field 'issue_status', 'name' %></p>
7
7
8 <% if Issue.use_status_for_done_ratio? %>
9 <p><label for="issue_done_ratio"><%=l(:field_done_ratio)%></label>
10 <%= select 'issue_status', :default_done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
11 <% end %>
12
8 <p><label for="issue_status_is_closed"><%=l(:field_is_closed)%></label>
13 <p><label for="issue_status_is_closed"><%=l(:field_is_closed)%></label>
9 <%= check_box 'issue_status', 'is_closed' %></p>
14 <%= check_box 'issue_status', 'is_closed' %></p>
10
15
11 <p><label for="issue_status_is_default"><%=l(:field_is_default)%></label>
16 <p><label for="issue_status_is_default"><%=l(:field_is_default)%></label>
12 <%= check_box 'issue_status', 'is_default' %></p>
17 <%= check_box 'issue_status', 'is_default' %></p>
13
18
14 <%= call_hook(:view_issue_statuses_form, :issue_status => @issue_status) %>
19 <%= call_hook(:view_issue_statuses_form, :issue_status => @issue_status) %>
15
20
16 <!--[eoform:issue_status]-->
21 <!--[eoform:issue_status]-->
17 </div>
22 </div>
@@ -1,35 +1,42
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to l(:label_issue_status_new), {:action => 'new'}, :class => 'icon icon-add' %>
2 <%= link_to l(:label_issue_status_new), {:action => 'new'}, :class => 'icon icon-add' %>
3 <%= link_to(l(:label_update_issue_done_ratios), {:action => 'update_issue_done_ratio'}, :class => 'icon icon-multiple', :method => 'post', :confirm => l(:text_are_you_sure)) if Issue.use_status_for_done_ratio? %>
3 </div>
4 </div>
4
5
5 <h2><%=l(:label_issue_status_plural)%></h2>
6 <h2><%=l(:label_issue_status_plural)%></h2>
6
7
7 <table class="list">
8 <table class="list">
8 <thead><tr>
9 <thead><tr>
9 <th><%=l(:field_status)%></th>
10 <th><%=l(:field_status)%></th>
11 <% if Issue.use_status_for_done_ratio? %>
12 <th><%=l(:field_done_ratio)%></th>
13 <% end %>
10 <th><%=l(:field_is_default)%></th>
14 <th><%=l(:field_is_default)%></th>
11 <th><%=l(:field_is_closed)%></th>
15 <th><%=l(:field_is_closed)%></th>
12 <th><%=l(:button_sort)%></th>
16 <th><%=l(:button_sort)%></th>
13 <th></th>
17 <th></th>
14 </tr></thead>
18 </tr></thead>
15 <tbody>
19 <tbody>
16 <% for status in @issue_statuses %>
20 <% for status in @issue_statuses %>
17 <tr class="<%= cycle("odd", "even") %>">
21 <tr class="<%= cycle("odd", "even") %>">
18 <td><%= link_to status.name, :action => 'edit', :id => status %></td>
22 <td><%= link_to status.name, :action => 'edit', :id => status %></td>
23 <% if Issue.use_status_for_done_ratio? %>
24 <td align="center"><%= h status.default_done_ratio %></td>
25 <% end %>
19 <td align="center"><%= image_tag 'true.png' if status.is_default? %></td>
26 <td align="center"><%= image_tag 'true.png' if status.is_default? %></td>
20 <td align="center"><%= image_tag 'true.png' if status.is_closed? %></td>
27 <td align="center"><%= image_tag 'true.png' if status.is_closed? %></td>
21 <td align="center" style="width:15%;"><%= reorder_links('issue_status', {:action => 'update', :id => status}) %></td>
28 <td align="center" style="width:15%;"><%= reorder_links('issue_status', {:action => 'update', :id => status}) %></td>
22 <td class="buttons">
29 <td class="buttons">
23 <%= link_to(l(:button_delete), { :action => 'destroy', :id => status },
30 <%= link_to(l(:button_delete), { :action => 'destroy', :id => status },
24 :method => :post,
31 :method => :post,
25 :confirm => l(:text_are_you_sure),
32 :confirm => l(:text_are_you_sure),
26 :class => 'icon icon-del') %>
33 :class => 'icon icon-del') %>
27 </td>
34 </td>
28 </tr>
35 </tr>
29 <% end %>
36 <% end %>
30 </tbody>
37 </tbody>
31 </table>
38 </table>
32
39
33 <p class="pagination"><%= pagination_links_full @issue_status_pages %></p>
40 <p class="pagination"><%= pagination_links_full @issue_status_pages %></p>
34
41
35 <% html_title(l(:label_issue_status_plural)) -%>
42 <% html_title(l(:label_issue_status_plural)) -%>
@@ -1,43 +1,45
1 <% fields_for :issue, @issue, :builder => TabularFormBuilder do |f| %>
1 <% fields_for :issue, @issue, :builder => TabularFormBuilder do |f| %>
2
2
3 <div class="splitcontentleft">
3 <div class="splitcontentleft">
4 <% if @issue.new_record? || @allowed_statuses.any? %>
4 <% if @issue.new_record? || @allowed_statuses.any? %>
5 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
5 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
6 <% else %>
6 <% else %>
7 <p><label><%= l(:field_status) %></label> <%= @issue.status.name %></p>
7 <p><label><%= l(:field_status) %></label> <%= @issue.status.name %></p>
8 <% end %>
8 <% end %>
9
9
10 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
10 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
11 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
11 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
12 <% unless @project.issue_categories.empty? %>
12 <% unless @project.issue_categories.empty? %>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
14 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
14 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
15 l(:label_issue_category_new),
15 l(:label_issue_category_new),
16 'category[name]',
16 'category[name]',
17 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
17 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
18 :title => l(:label_issue_category_new),
18 :title => l(:label_issue_category_new),
19 :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
19 :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
20 <% end %>
20 <% end %>
21 <% unless @issue.assignable_versions.empty? %>
21 <% unless @issue.assignable_versions.empty? %>
22 <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
22 <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
23 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
23 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
24 l(:label_version_new),
24 l(:label_version_new),
25 'version[name]',
25 'version[name]',
26 {:controller => 'projects', :action => 'add_version', :id => @project},
26 {:controller => 'projects', :action => 'add_version', :id => @project},
27 :title => l(:label_version_new),
27 :title => l(:label_version_new),
28 :tabindex => 200) if authorize_for('projects', 'add_version') %>
28 :tabindex => 200) if authorize_for('projects', 'add_version') %>
29 </p>
29 </p>
30 <% end %>
30 <% end %>
31 </div>
31 </div>
32
32
33 <div class="splitcontentright">
33 <div class="splitcontentright">
34 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
34 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
35 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
35 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
36 <p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p>
36 <p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p>
37 <% if Issue.use_field_for_done_ratio? %>
37 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
38 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
39 <% end %>
38 </div>
40 </div>
39
41
40 <div style="clear:both;"> </div>
42 <div style="clear:both;"> </div>
41 <%= render :partial => 'form_custom_fields' %>
43 <%= render :partial => 'form_custom_fields' %>
42
44
43 <% end %>
45 <% end %>
@@ -1,12 +1,14
1 <div class="attributes">
1 <div class="attributes">
2 <div class="splitcontentleft">
2 <div class="splitcontentleft">
3 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
3 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
4 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
4 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
5 </div>
5 </div>
6 <div class="splitcontentright">
6 <div class="splitcontentright">
7 <% if Issue.use_field_for_done_ratio? %>
7 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
8 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
9 <% end %>
8 <% unless @issue.assignable_versions.empty? %>
10 <% unless @issue.assignable_versions.empty? %>
9 <p><%= f.select :fixed_version_id, (@issue.assignable_versions.collect {|v| [v.name, v.id]}), :include_blank => true %></p>
11 <p><%= f.select :fixed_version_id, (@issue.assignable_versions.collect {|v| [v.name, v.id]}), :include_blank => true %></p>
10 <% end %>
12 <% end %>
11 </div>
13 </div>
12 </div>
14 </div>
@@ -1,62 +1,64
1 <h2><%= l(:label_bulk_edit_selected_issues) %></h2>
1 <h2><%= l(:label_bulk_edit_selected_issues) %></h2>
2
2
3 <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
3 <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
4
4
5 <% form_tag() do %>
5 <% form_tag() do %>
6 <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
6 <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
7 <div class="box">
7 <div class="box">
8 <fieldset>
8 <fieldset>
9 <legend><%= l(:label_change_properties) %></legend>
9 <legend><%= l(:label_change_properties) %></legend>
10 <p>
10 <p>
11 <label><%= l(:field_tracker) %>:
11 <label><%= l(:field_tracker) %>:
12 <%= select_tag('tracker_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@project.trackers, :id, :name)) %></label>
12 <%= select_tag('tracker_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@project.trackers, :id, :name)) %></label>
13 <% if @available_statuses.any? %>
13 <% if @available_statuses.any? %>
14 <label><%= l(:field_status) %>:
14 <label><%= l(:field_status) %>:
15 <%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %></label>
15 <%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %></label>
16 <% end %>
16 <% end %>
17 </p>
17 </p>
18 <p>
18 <p>
19 <label><%= l(:field_priority) %>:
19 <label><%= l(:field_priority) %>:
20 <%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %></label>
20 <%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %></label>
21 <label><%= l(:field_category) %>:
21 <label><%= l(:field_category) %>:
22 <%= select_tag('category_id', content_tag('option', l(:label_no_change_option), :value => '') +
22 <%= select_tag('category_id', content_tag('option', l(:label_no_change_option), :value => '') +
23 content_tag('option', l(:label_none), :value => 'none') +
23 content_tag('option', l(:label_none), :value => 'none') +
24 options_from_collection_for_select(@project.issue_categories, :id, :name)) %></label>
24 options_from_collection_for_select(@project.issue_categories, :id, :name)) %></label>
25 </p>
25 </p>
26 <p>
26 <p>
27 <label><%= l(:field_assigned_to) %>:
27 <label><%= l(:field_assigned_to) %>:
28 <%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') +
28 <%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') +
29 content_tag('option', l(:label_nobody), :value => 'none') +
29 content_tag('option', l(:label_nobody), :value => 'none') +
30 options_from_collection_for_select(@project.assignable_users, :id, :name)) %></label>
30 options_from_collection_for_select(@project.assignable_users, :id, :name)) %></label>
31 <label><%= l(:field_fixed_version) %>:
31 <label><%= l(:field_fixed_version) %>:
32 <%= select_tag('fixed_version_id', content_tag('option', l(:label_no_change_option), :value => '') +
32 <%= select_tag('fixed_version_id', content_tag('option', l(:label_no_change_option), :value => '') +
33 content_tag('option', l(:label_none), :value => 'none') +
33 content_tag('option', l(:label_none), :value => 'none') +
34 version_options_for_select(@project.shared_versions.open)) %></label>
34 version_options_for_select(@project.shared_versions.open)) %></label>
35 </p>
35 </p>
36
36
37 <p>
37 <p>
38 <label><%= l(:field_start_date) %>:
38 <label><%= l(:field_start_date) %>:
39 <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %></label>
39 <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %></label>
40 <label><%= l(:field_due_date) %>:
40 <label><%= l(:field_due_date) %>:
41 <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %></label>
41 <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %></label>
42 <% if Issue.use_field_for_done_ratio? %>
42 <label><%= l(:field_done_ratio) %>:
43 <label><%= l(:field_done_ratio) %>:
43 <%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label>
44 <%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label>
45 <% end %>
44 </p>
46 </p>
45
47
46 <% @custom_fields.each do |custom_field| %>
48 <% @custom_fields.each do |custom_field| %>
47 <p><label><%= h(custom_field.name) %></label>
49 <p><label><%= h(custom_field.name) %></label>
48 <%= select_tag "custom_field_values[#{custom_field.id}]", options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values) %></label>
50 <%= select_tag "custom_field_values[#{custom_field.id}]", options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values) %></label>
49 </p>
51 </p>
50 <% end %>
52 <% end %>
51
53
52 <%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
54 <%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
53 </fieldset>
55 </fieldset>
54
56
55 <fieldset><legend><%= l(:field_notes) %></legend>
57 <fieldset><legend><%= l(:field_notes) %></legend>
56 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
58 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
57 <%= wikitoolbar_for 'notes' %>
59 <%= wikitoolbar_for 'notes' %>
58 </fieldset>
60 </fieldset>
59 </div>
61 </div>
60
62
61 <p><%= submit_tag l(:button_submit) %>
63 <p><%= submit_tag l(:button_submit) %>
62 <% end %>
64 <% end %>
@@ -1,112 +1,113
1 <ul>
1 <ul>
2 <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
2 <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
3
3
4 <% if !@issue.nil? -%>
4 <% if !@issue.nil? -%>
5 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
5 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
6 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
6 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
7 <li class="folder">
7 <li class="folder">
8 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
8 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
9 <ul>
9 <ul>
10 <% @statuses.each do |s| -%>
10 <% @statuses.each do |s| -%>
11 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'edit', :id => @issue, :issue => {:status_id => s}, :back_to => @back}, :method => :post,
11 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'edit', :id => @issue, :issue => {:status_id => s}, :back_to => @back}, :method => :post,
12 :selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
12 :selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
13 <% end -%>
13 <% end -%>
14 </ul>
14 </ul>
15 </li>
15 </li>
16 <% else %>
16 <% else %>
17 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
17 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
18 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
18 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
19 <% end %>
19 <% end %>
20
20
21 <% unless @trackers.nil? %>
21 <% unless @trackers.nil? %>
22 <li class="folder">
22 <li class="folder">
23 <a href="#" class="submenu"><%= l(:field_tracker) %></a>
23 <a href="#" class="submenu"><%= l(:field_tracker) %></a>
24 <ul>
24 <ul>
25 <% @trackers.each do |t| -%>
25 <% @trackers.each do |t| -%>
26 <li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'tracker_id' => t, :back_to => @back}, :method => :post,
26 <li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'tracker_id' => t, :back_to => @back}, :method => :post,
27 :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
27 :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
28 <% end -%>
28 <% end -%>
29 </ul>
29 </ul>
30 </li>
30 </li>
31 <% end %>
31 <% end %>
32 <li class="folder">
32 <li class="folder">
33 <a href="#" class="submenu"><%= l(:field_priority) %></a>
33 <a href="#" class="submenu"><%= l(:field_priority) %></a>
34 <ul>
34 <ul>
35 <% @priorities.each do |p| -%>
35 <% @priorities.each do |p| -%>
36 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'priority_id' => p, :back_to => @back}, :method => :post,
36 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'priority_id' => p, :back_to => @back}, :method => :post,
37 :selected => (@issue && p == @issue.priority), :disabled => !@can[:edit] %></li>
37 :selected => (@issue && p == @issue.priority), :disabled => !@can[:edit] %></li>
38 <% end -%>
38 <% end -%>
39 </ul>
39 </ul>
40 </li>
40 </li>
41 <% unless @project.nil? || @project.shared_versions.open.empty? -%>
41 <% unless @project.nil? || @project.shared_versions.open.empty? -%>
42 <li class="folder">
42 <li class="folder">
43 <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
43 <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
44 <ul>
44 <ul>
45 <% @project.shared_versions.open.sort.each do |v| -%>
45 <% @project.shared_versions.open.sort.each do |v| -%>
46 <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_to => @back}, :method => :post,
46 <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_to => @back}, :method => :post,
47 :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
47 :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
48 <% end -%>
48 <% end -%>
49 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => 'none', :back_to => @back}, :method => :post,
49 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => 'none', :back_to => @back}, :method => :post,
50 :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
50 :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
51 </ul>
51 </ul>
52 </li>
52 </li>
53 <% end %>
53 <% end %>
54 <% unless @assignables.nil? || @assignables.empty? -%>
54 <% unless @assignables.nil? || @assignables.empty? -%>
55 <li class="folder">
55 <li class="folder">
56 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
56 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
57 <ul>
57 <ul>
58 <% @assignables.each do |u| -%>
58 <% @assignables.each do |u| -%>
59 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => u, :back_to => @back}, :method => :post,
59 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => u, :back_to => @back}, :method => :post,
60 :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
60 :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
61 <% end -%>
61 <% end -%>
62 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => 'none', :back_to => @back}, :method => :post,
62 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => 'none', :back_to => @back}, :method => :post,
63 :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
63 :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
64 </ul>
64 </ul>
65 </li>
65 </li>
66 <% end %>
66 <% end %>
67 <% unless @project.nil? || @project.issue_categories.empty? -%>
67 <% unless @project.nil? || @project.issue_categories.empty? -%>
68 <li class="folder">
68 <li class="folder">
69 <a href="#" class="submenu"><%= l(:field_category) %></a>
69 <a href="#" class="submenu"><%= l(:field_category) %></a>
70 <ul>
70 <ul>
71 <% @project.issue_categories.each do |u| -%>
71 <% @project.issue_categories.each do |u| -%>
72 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => u, :back_to => @back}, :method => :post,
72 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => u, :back_to => @back}, :method => :post,
73 :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
73 :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
74 <% end -%>
74 <% end -%>
75 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => 'none', :back_to => @back}, :method => :post,
75 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => 'none', :back_to => @back}, :method => :post,
76 :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
76 :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
77 </ul>
77 </ul>
78 </li>
78 </li>
79 <% end -%>
79 <% end -%>
80 <% if Issue.use_field_for_done_ratio? %>
80 <li class="folder">
81 <li class="folder">
81 <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
82 <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
82 <ul>
83 <ul>
83 <% (0..10).map{|x|x*10}.each do |p| -%>
84 <% (0..10).map{|x|x*10}.each do |p| -%>
84 <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'done_ratio' => p, :back_to => @back}, :method => :post,
85 <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'done_ratio' => p, :back_to => @back}, :method => :post,
85 :selected => (@issue && p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
86 :selected => (@issue && p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
86 <% end -%>
87 <% end -%>
87 </ul>
88 </ul>
88 </li>
89 </li>
89
90 <% end %>
90 <% if !@issue.nil? %>
91 <% if !@issue.nil? %>
91 <% if @can[:log_time] -%>
92 <% if @can[:log_time] -%>
92 <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
93 <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
93 :class => 'icon-time-add' %></li>
94 :class => 'icon-time-add' %></li>
94 <% end %>
95 <% end %>
95 <% if User.current.logged? %>
96 <% if User.current.logged? %>
96 <li><%= watcher_link(@issue, User.current) %></li>
97 <li><%= watcher_link(@issue, User.current) %></li>
97 <% end %>
98 <% end %>
98 <% end %>
99 <% end %>
99
100
100 <% if @issue.present? %>
101 <% if @issue.present? %>
101 <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
102 <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
102 :class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
103 :class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
103 <% end %>
104 <% end %>
104 <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id), :copy_options => {:copy => 't'}},
105 <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id), :copy_options => {:copy => 't'}},
105 :class => 'icon-copy', :disabled => !@can[:move] %></li>
106 :class => 'icon-copy', :disabled => !@can[:move] %></li>
106 <li><%= context_menu_link l(:button_move), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id)},
107 <li><%= context_menu_link l(:button_move), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id)},
107 :class => 'icon-move', :disabled => !@can[:move] %></li>
108 :class => 'icon-move', :disabled => !@can[:move] %></li>
108 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
109 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
109 :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
110 :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
110
111
111 <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
112 <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
112 </ul>
113 </ul>
@@ -1,27 +1,30
1 <% form_tag({:action => 'edit', :tab => 'issues'}) do %>
1 <% form_tag({:action => 'edit', :tab => 'issues'}) do %>
2
2
3 <div class="box tabular settings">
3 <div class="box tabular settings">
4 <p><label><%= l(:setting_cross_project_issue_relations) %></label>
4 <p><label><%= l(:setting_cross_project_issue_relations) %></label>
5 <%= hidden_field_tag 'settings[cross_project_issue_relations]', 0 %>
5 <%= hidden_field_tag 'settings[cross_project_issue_relations]', 0 %>
6 <%= check_box_tag 'settings[cross_project_issue_relations]', 1, Setting.cross_project_issue_relations? %>
6 <%= check_box_tag 'settings[cross_project_issue_relations]', 1, Setting.cross_project_issue_relations? %>
7 </p>
7 </p>
8
8
9 <p><label><%= l(:setting_display_subprojects_issues) %></label>
9 <p><label><%= l(:setting_display_subprojects_issues) %></label>
10 <%= hidden_field_tag 'settings[display_subprojects_issues]', 0 %>
10 <%= hidden_field_tag 'settings[display_subprojects_issues]', 0 %>
11 <%= check_box_tag 'settings[display_subprojects_issues]', 1, Setting.display_subprojects_issues? %>
11 <%= check_box_tag 'settings[display_subprojects_issues]', 1, Setting.display_subprojects_issues? %>
12 </p>
12 </p>
13
13
14 <p><label><%= l(:setting_issue_done_ratio) %></label>
15 <%= select_tag 'settings[issue_done_ratio]', options_for_select(Issue::DONE_RATIO_OPTIONS.collect {|i| [l(i.to_sym), i]}, Setting.issue_done_ratio) %></p>
16
14 <p><label><%= l(:setting_issues_export_limit) %></label>
17 <p><label><%= l(:setting_issues_export_limit) %></label>
15 <%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p>
18 <%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p>
16 </div>
19 </div>
17
20
18 <fieldset class="box settings"><legend><%= l(:setting_issue_list_default_columns) %></legend>
21 <fieldset class="box settings"><legend><%= l(:setting_issue_list_default_columns) %></legend>
19 <%= hidden_field_tag 'settings[issue_list_default_columns][]', '' %>
22 <%= hidden_field_tag 'settings[issue_list_default_columns][]', '' %>
20 <% Query.new.available_columns.each do |column| %>
23 <% Query.new.available_columns.each do |column| %>
21 <label><%= check_box_tag 'settings[issue_list_default_columns][]', column.name, Setting.issue_list_default_columns.include?(column.name.to_s) %>
24 <label><%= check_box_tag 'settings[issue_list_default_columns][]', column.name, Setting.issue_list_default_columns.include?(column.name.to_s) %>
22 <%= column.caption %></label><br />
25 <%= column.caption %></label><br />
23 <% end %>
26 <% end %>
24 </fieldset>
27 </fieldset>
25
28
26 <%= submit_tag l(:button_save) %>
29 <%= submit_tag l(:button_save) %>
27 <% end %>
30 <% end %>
@@ -1,852 +1,859
1 en:
1 en:
2 date:
2 date:
3 formats:
3 formats:
4 # Use the strftime parameters for formats.
4 # Use the strftime parameters for formats.
5 # When no format has been given, it uses default.
5 # When no format has been given, it uses default.
6 # You can provide other formats here if you like!
6 # You can provide other formats here if you like!
7 default: "%m/%d/%Y"
7 default: "%m/%d/%Y"
8 short: "%b %d"
8 short: "%b %d"
9 long: "%B %d, %Y"
9 long: "%B %d, %Y"
10
10
11 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
11 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
12 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
12 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
13
13
14 # Don't forget the nil at the beginning; there's no such thing as a 0th month
14 # Don't forget the nil at the beginning; there's no such thing as a 0th month
15 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
15 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
16 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
16 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
17 # Used in date_select and datime_select.
17 # Used in date_select and datime_select.
18 order: [ :year, :month, :day ]
18 order: [ :year, :month, :day ]
19
19
20 time:
20 time:
21 formats:
21 formats:
22 default: "%m/%d/%Y %I:%M %p"
22 default: "%m/%d/%Y %I:%M %p"
23 time: "%I:%M %p"
23 time: "%I:%M %p"
24 short: "%d %b %H:%M"
24 short: "%d %b %H:%M"
25 long: "%B %d, %Y %H:%M"
25 long: "%B %d, %Y %H:%M"
26 am: "am"
26 am: "am"
27 pm: "pm"
27 pm: "pm"
28
28
29 datetime:
29 datetime:
30 distance_in_words:
30 distance_in_words:
31 half_a_minute: "half a minute"
31 half_a_minute: "half a minute"
32 less_than_x_seconds:
32 less_than_x_seconds:
33 one: "less than 1 second"
33 one: "less than 1 second"
34 other: "less than {{count}} seconds"
34 other: "less than {{count}} seconds"
35 x_seconds:
35 x_seconds:
36 one: "1 second"
36 one: "1 second"
37 other: "{{count}} seconds"
37 other: "{{count}} seconds"
38 less_than_x_minutes:
38 less_than_x_minutes:
39 one: "less than a minute"
39 one: "less than a minute"
40 other: "less than {{count}} minutes"
40 other: "less than {{count}} minutes"
41 x_minutes:
41 x_minutes:
42 one: "1 minute"
42 one: "1 minute"
43 other: "{{count}} minutes"
43 other: "{{count}} minutes"
44 about_x_hours:
44 about_x_hours:
45 one: "about 1 hour"
45 one: "about 1 hour"
46 other: "about {{count}} hours"
46 other: "about {{count}} hours"
47 x_days:
47 x_days:
48 one: "1 day"
48 one: "1 day"
49 other: "{{count}} days"
49 other: "{{count}} days"
50 about_x_months:
50 about_x_months:
51 one: "about 1 month"
51 one: "about 1 month"
52 other: "about {{count}} months"
52 other: "about {{count}} months"
53 x_months:
53 x_months:
54 one: "1 month"
54 one: "1 month"
55 other: "{{count}} months"
55 other: "{{count}} months"
56 about_x_years:
56 about_x_years:
57 one: "about 1 year"
57 one: "about 1 year"
58 other: "about {{count}} years"
58 other: "about {{count}} years"
59 over_x_years:
59 over_x_years:
60 one: "over 1 year"
60 one: "over 1 year"
61 other: "over {{count}} years"
61 other: "over {{count}} years"
62
62
63 number:
63 number:
64 human:
64 human:
65 format:
65 format:
66 delimiter: ""
66 delimiter: ""
67 precision: 1
67 precision: 1
68 storage_units:
68 storage_units:
69 format: "%n %u"
69 format: "%n %u"
70 units:
70 units:
71 byte:
71 byte:
72 one: "Byte"
72 one: "Byte"
73 other: "Bytes"
73 other: "Bytes"
74 kb: "KB"
74 kb: "KB"
75 mb: "MB"
75 mb: "MB"
76 gb: "GB"
76 gb: "GB"
77 tb: "TB"
77 tb: "TB"
78
78
79
79
80 # Used in array.to_sentence.
80 # Used in array.to_sentence.
81 support:
81 support:
82 array:
82 array:
83 sentence_connector: "and"
83 sentence_connector: "and"
84 skip_last_comma: false
84 skip_last_comma: false
85
85
86 activerecord:
86 activerecord:
87 errors:
87 errors:
88 messages:
88 messages:
89 inclusion: "is not included in the list"
89 inclusion: "is not included in the list"
90 exclusion: "is reserved"
90 exclusion: "is reserved"
91 invalid: "is invalid"
91 invalid: "is invalid"
92 confirmation: "doesn't match confirmation"
92 confirmation: "doesn't match confirmation"
93 accepted: "must be accepted"
93 accepted: "must be accepted"
94 empty: "can't be empty"
94 empty: "can't be empty"
95 blank: "can't be blank"
95 blank: "can't be blank"
96 too_long: "is too long (maximum is {{count}} characters)"
96 too_long: "is too long (maximum is {{count}} characters)"
97 too_short: "is too short (minimum is {{count}} characters)"
97 too_short: "is too short (minimum is {{count}} characters)"
98 wrong_length: "is the wrong length (should be {{count}} characters)"
98 wrong_length: "is the wrong length (should be {{count}} characters)"
99 taken: "has already been taken"
99 taken: "has already been taken"
100 not_a_number: "is not a number"
100 not_a_number: "is not a number"
101 not_a_date: "is not a valid date"
101 not_a_date: "is not a valid date"
102 greater_than: "must be greater than {{count}}"
102 greater_than: "must be greater than {{count}}"
103 greater_than_or_equal_to: "must be greater than or equal to {{count}}"
103 greater_than_or_equal_to: "must be greater than or equal to {{count}}"
104 equal_to: "must be equal to {{count}}"
104 equal_to: "must be equal to {{count}}"
105 less_than: "must be less than {{count}}"
105 less_than: "must be less than {{count}}"
106 less_than_or_equal_to: "must be less than or equal to {{count}}"
106 less_than_or_equal_to: "must be less than or equal to {{count}}"
107 odd: "must be odd"
107 odd: "must be odd"
108 even: "must be even"
108 even: "must be even"
109 greater_than_start_date: "must be greater than start date"
109 greater_than_start_date: "must be greater than start date"
110 not_same_project: "doesn't belong to the same project"
110 not_same_project: "doesn't belong to the same project"
111 circular_dependency: "This relation would create a circular dependency"
111 circular_dependency: "This relation would create a circular dependency"
112
112
113 actionview_instancetag_blank_option: Please select
113 actionview_instancetag_blank_option: Please select
114
114
115 general_text_No: 'No'
115 general_text_No: 'No'
116 general_text_Yes: 'Yes'
116 general_text_Yes: 'Yes'
117 general_text_no: 'no'
117 general_text_no: 'no'
118 general_text_yes: 'yes'
118 general_text_yes: 'yes'
119 general_lang_name: 'English'
119 general_lang_name: 'English'
120 general_csv_separator: ','
120 general_csv_separator: ','
121 general_csv_decimal_separator: '.'
121 general_csv_decimal_separator: '.'
122 general_csv_encoding: ISO-8859-1
122 general_csv_encoding: ISO-8859-1
123 general_pdf_encoding: ISO-8859-1
123 general_pdf_encoding: ISO-8859-1
124 general_first_day_of_week: '7'
124 general_first_day_of_week: '7'
125
125
126 notice_account_updated: Account was successfully updated.
126 notice_account_updated: Account was successfully updated.
127 notice_account_invalid_creditentials: Invalid user or password
127 notice_account_invalid_creditentials: Invalid user or password
128 notice_account_password_updated: Password was successfully updated.
128 notice_account_password_updated: Password was successfully updated.
129 notice_account_wrong_password: Wrong password
129 notice_account_wrong_password: Wrong password
130 notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
130 notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
131 notice_account_unknown_email: Unknown user.
131 notice_account_unknown_email: Unknown user.
132 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
132 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
133 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
133 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
134 notice_account_activated: Your account has been activated. You can now log in.
134 notice_account_activated: Your account has been activated. You can now log in.
135 notice_successful_create: Successful creation.
135 notice_successful_create: Successful creation.
136 notice_successful_update: Successful update.
136 notice_successful_update: Successful update.
137 notice_successful_delete: Successful deletion.
137 notice_successful_delete: Successful deletion.
138 notice_successful_connection: Successful connection.
138 notice_successful_connection: Successful connection.
139 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
139 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
140 notice_locking_conflict: Data has been updated by another user.
140 notice_locking_conflict: Data has been updated by another user.
141 notice_not_authorized: You are not authorized to access this page.
141 notice_not_authorized: You are not authorized to access this page.
142 notice_email_sent: "An email was sent to {{value}}"
142 notice_email_sent: "An email was sent to {{value}}"
143 notice_email_error: "An error occurred while sending mail ({{value}})"
143 notice_email_error: "An error occurred while sending mail ({{value}})"
144 notice_feeds_access_key_reseted: Your RSS access key was reset.
144 notice_feeds_access_key_reseted: Your RSS access key was reset.
145 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
145 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
146 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
146 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
147 notice_account_pending: "Your account was created and is now pending administrator approval."
147 notice_account_pending: "Your account was created and is now pending administrator approval."
148 notice_default_data_loaded: Default configuration successfully loaded.
148 notice_default_data_loaded: Default configuration successfully loaded.
149 notice_unable_delete_version: Unable to delete version.
149 notice_unable_delete_version: Unable to delete version.
150 notice_issue_done_ratios_updated: Issue done ratios updated.
150
151
151 error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
152 error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
152 error_scm_not_found: "The entry or revision was not found in the repository."
153 error_scm_not_found: "The entry or revision was not found in the repository."
153 error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
154 error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
154 error_scm_annotate: "The entry does not exist or can not be annotated."
155 error_scm_annotate: "The entry does not exist or can not be annotated."
155 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
156 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
156 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
157 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
157 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
158 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
158 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
159 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
159 error_can_not_archive_project: This project can not be archived
160 error_can_not_archive_project: This project can not be archived
160
161 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
162
161 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
163 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
162
164
163 mail_subject_lost_password: "Your {{value}} password"
165 mail_subject_lost_password: "Your {{value}} password"
164 mail_body_lost_password: 'To change your password, click on the following link:'
166 mail_body_lost_password: 'To change your password, click on the following link:'
165 mail_subject_register: "Your {{value}} account activation"
167 mail_subject_register: "Your {{value}} account activation"
166 mail_body_register: 'To activate your account, click on the following link:'
168 mail_body_register: 'To activate your account, click on the following link:'
167 mail_body_account_information_external: "You can use your {{value}} account to log in."
169 mail_body_account_information_external: "You can use your {{value}} account to log in."
168 mail_body_account_information: Your account information
170 mail_body_account_information: Your account information
169 mail_subject_account_activation_request: "{{value}} account activation request"
171 mail_subject_account_activation_request: "{{value}} account activation request"
170 mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
172 mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
171 mail_subject_reminder: "{{count}} issue(s) due in the next days"
173 mail_subject_reminder: "{{count}} issue(s) due in the next days"
172 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
174 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
173 mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
175 mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
174 mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}."
176 mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}."
175 mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
177 mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
176 mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}."
178 mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}."
177
179
178 gui_validation_error: 1 error
180 gui_validation_error: 1 error
179 gui_validation_error_plural: "{{count}} errors"
181 gui_validation_error_plural: "{{count}} errors"
180
182
181 field_name: Name
183 field_name: Name
182 field_description: Description
184 field_description: Description
183 field_summary: Summary
185 field_summary: Summary
184 field_is_required: Required
186 field_is_required: Required
185 field_firstname: Firstname
187 field_firstname: Firstname
186 field_lastname: Lastname
188 field_lastname: Lastname
187 field_mail: Email
189 field_mail: Email
188 field_filename: File
190 field_filename: File
189 field_filesize: Size
191 field_filesize: Size
190 field_downloads: Downloads
192 field_downloads: Downloads
191 field_author: Author
193 field_author: Author
192 field_created_on: Created
194 field_created_on: Created
193 field_updated_on: Updated
195 field_updated_on: Updated
194 field_field_format: Format
196 field_field_format: Format
195 field_is_for_all: For all projects
197 field_is_for_all: For all projects
196 field_possible_values: Possible values
198 field_possible_values: Possible values
197 field_regexp: Regular expression
199 field_regexp: Regular expression
198 field_min_length: Minimum length
200 field_min_length: Minimum length
199 field_max_length: Maximum length
201 field_max_length: Maximum length
200 field_value: Value
202 field_value: Value
201 field_category: Category
203 field_category: Category
202 field_title: Title
204 field_title: Title
203 field_project: Project
205 field_project: Project
204 field_issue: Issue
206 field_issue: Issue
205 field_status: Status
207 field_status: Status
206 field_notes: Notes
208 field_notes: Notes
207 field_is_closed: Issue closed
209 field_is_closed: Issue closed
208 field_is_default: Default value
210 field_is_default: Default value
209 field_tracker: Tracker
211 field_tracker: Tracker
210 field_subject: Subject
212 field_subject: Subject
211 field_due_date: Due date
213 field_due_date: Due date
212 field_assigned_to: Assigned to
214 field_assigned_to: Assigned to
213 field_priority: Priority
215 field_priority: Priority
214 field_fixed_version: Target version
216 field_fixed_version: Target version
215 field_user: User
217 field_user: User
216 field_role: Role
218 field_role: Role
217 field_homepage: Homepage
219 field_homepage: Homepage
218 field_is_public: Public
220 field_is_public: Public
219 field_parent: Subproject of
221 field_parent: Subproject of
220 field_is_in_chlog: Issues displayed in changelog
222 field_is_in_chlog: Issues displayed in changelog
221 field_is_in_roadmap: Issues displayed in roadmap
223 field_is_in_roadmap: Issues displayed in roadmap
222 field_login: Login
224 field_login: Login
223 field_mail_notification: Email notifications
225 field_mail_notification: Email notifications
224 field_admin: Administrator
226 field_admin: Administrator
225 field_last_login_on: Last connection
227 field_last_login_on: Last connection
226 field_language: Language
228 field_language: Language
227 field_effective_date: Date
229 field_effective_date: Date
228 field_password: Password
230 field_password: Password
229 field_new_password: New password
231 field_new_password: New password
230 field_password_confirmation: Confirmation
232 field_password_confirmation: Confirmation
231 field_version: Version
233 field_version: Version
232 field_type: Type
234 field_type: Type
233 field_host: Host
235 field_host: Host
234 field_port: Port
236 field_port: Port
235 field_account: Account
237 field_account: Account
236 field_base_dn: Base DN
238 field_base_dn: Base DN
237 field_attr_login: Login attribute
239 field_attr_login: Login attribute
238 field_attr_firstname: Firstname attribute
240 field_attr_firstname: Firstname attribute
239 field_attr_lastname: Lastname attribute
241 field_attr_lastname: Lastname attribute
240 field_attr_mail: Email attribute
242 field_attr_mail: Email attribute
241 field_onthefly: On-the-fly user creation
243 field_onthefly: On-the-fly user creation
242 field_start_date: Start
244 field_start_date: Start
243 field_done_ratio: % Done
245 field_done_ratio: % Done
244 field_auth_source: Authentication mode
246 field_auth_source: Authentication mode
245 field_hide_mail: Hide my email address
247 field_hide_mail: Hide my email address
246 field_comments: Comment
248 field_comments: Comment
247 field_url: URL
249 field_url: URL
248 field_start_page: Start page
250 field_start_page: Start page
249 field_subproject: Subproject
251 field_subproject: Subproject
250 field_hours: Hours
252 field_hours: Hours
251 field_activity: Activity
253 field_activity: Activity
252 field_spent_on: Date
254 field_spent_on: Date
253 field_identifier: Identifier
255 field_identifier: Identifier
254 field_is_filter: Used as a filter
256 field_is_filter: Used as a filter
255 field_issue_to: Related issue
257 field_issue_to: Related issue
256 field_delay: Delay
258 field_delay: Delay
257 field_assignable: Issues can be assigned to this role
259 field_assignable: Issues can be assigned to this role
258 field_redirect_existing_links: Redirect existing links
260 field_redirect_existing_links: Redirect existing links
259 field_estimated_hours: Estimated time
261 field_estimated_hours: Estimated time
260 field_column_names: Columns
262 field_column_names: Columns
261 field_time_zone: Time zone
263 field_time_zone: Time zone
262 field_searchable: Searchable
264 field_searchable: Searchable
263 field_default_value: Default value
265 field_default_value: Default value
264 field_comments_sorting: Display comments
266 field_comments_sorting: Display comments
265 field_parent_title: Parent page
267 field_parent_title: Parent page
266 field_editable: Editable
268 field_editable: Editable
267 field_watcher: Watcher
269 field_watcher: Watcher
268 field_identity_url: OpenID URL
270 field_identity_url: OpenID URL
269 field_content: Content
271 field_content: Content
270 field_group_by: Group results by
272 field_group_by: Group results by
271 field_sharing: Sharing
273 field_sharing: Sharing
272
274
273 setting_app_title: Application title
275 setting_app_title: Application title
274 setting_app_subtitle: Application subtitle
276 setting_app_subtitle: Application subtitle
275 setting_welcome_text: Welcome text
277 setting_welcome_text: Welcome text
276 setting_default_language: Default language
278 setting_default_language: Default language
277 setting_login_required: Authentication required
279 setting_login_required: Authentication required
278 setting_self_registration: Self-registration
280 setting_self_registration: Self-registration
279 setting_attachment_max_size: Attachment max. size
281 setting_attachment_max_size: Attachment max. size
280 setting_issues_export_limit: Issues export limit
282 setting_issues_export_limit: Issues export limit
281 setting_mail_from: Emission email address
283 setting_mail_from: Emission email address
282 setting_bcc_recipients: Blind carbon copy recipients (bcc)
284 setting_bcc_recipients: Blind carbon copy recipients (bcc)
283 setting_plain_text_mail: Plain text mail (no HTML)
285 setting_plain_text_mail: Plain text mail (no HTML)
284 setting_host_name: Host name and path
286 setting_host_name: Host name and path
285 setting_text_formatting: Text formatting
287 setting_text_formatting: Text formatting
286 setting_wiki_compression: Wiki history compression
288 setting_wiki_compression: Wiki history compression
287 setting_feeds_limit: Feed content limit
289 setting_feeds_limit: Feed content limit
288 setting_default_projects_public: New projects are public by default
290 setting_default_projects_public: New projects are public by default
289 setting_autofetch_changesets: Autofetch commits
291 setting_autofetch_changesets: Autofetch commits
290 setting_sys_api_enabled: Enable WS for repository management
292 setting_sys_api_enabled: Enable WS for repository management
291 setting_commit_ref_keywords: Referencing keywords
293 setting_commit_ref_keywords: Referencing keywords
292 setting_commit_fix_keywords: Fixing keywords
294 setting_commit_fix_keywords: Fixing keywords
293 setting_autologin: Autologin
295 setting_autologin: Autologin
294 setting_date_format: Date format
296 setting_date_format: Date format
295 setting_time_format: Time format
297 setting_time_format: Time format
296 setting_cross_project_issue_relations: Allow cross-project issue relations
298 setting_cross_project_issue_relations: Allow cross-project issue relations
297 setting_issue_list_default_columns: Default columns displayed on the issue list
299 setting_issue_list_default_columns: Default columns displayed on the issue list
298 setting_repositories_encodings: Repositories encodings
300 setting_repositories_encodings: Repositories encodings
299 setting_commit_logs_encoding: Commit messages encoding
301 setting_commit_logs_encoding: Commit messages encoding
300 setting_emails_footer: Emails footer
302 setting_emails_footer: Emails footer
301 setting_protocol: Protocol
303 setting_protocol: Protocol
302 setting_per_page_options: Objects per page options
304 setting_per_page_options: Objects per page options
303 setting_user_format: Users display format
305 setting_user_format: Users display format
304 setting_activity_days_default: Days displayed on project activity
306 setting_activity_days_default: Days displayed on project activity
305 setting_display_subprojects_issues: Display subprojects issues on main projects by default
307 setting_display_subprojects_issues: Display subprojects issues on main projects by default
306 setting_enabled_scm: Enabled SCM
308 setting_enabled_scm: Enabled SCM
307 setting_mail_handler_api_enabled: Enable WS for incoming emails
309 setting_mail_handler_api_enabled: Enable WS for incoming emails
308 setting_mail_handler_api_key: API key
310 setting_mail_handler_api_key: API key
309 setting_sequential_project_identifiers: Generate sequential project identifiers
311 setting_sequential_project_identifiers: Generate sequential project identifiers
310 setting_gravatar_enabled: Use Gravatar user icons
312 setting_gravatar_enabled: Use Gravatar user icons
311 setting_gravatar_default: Default Gravatar image
313 setting_gravatar_default: Default Gravatar image
314 setting_issue_done_ratio: Calculate the issue done ratio with
312 setting_diff_max_lines_displayed: Max number of diff lines displayed
315 setting_diff_max_lines_displayed: Max number of diff lines displayed
313 setting_file_max_size_displayed: Max size of text files displayed inline
316 setting_file_max_size_displayed: Max size of text files displayed inline
314 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
317 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
315 setting_openid: Allow OpenID login and registration
318 setting_openid: Allow OpenID login and registration
316 setting_password_min_length: Minimum password length
319 setting_password_min_length: Minimum password length
317 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
320 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
318 setting_default_projects_modules: Default enabled modules for new projects
321 setting_default_projects_modules: Default enabled modules for new projects
319
322
320 permission_add_project: Create project
323 permission_add_project: Create project
321 permission_edit_project: Edit project
324 permission_edit_project: Edit project
322 permission_select_project_modules: Select project modules
325 permission_select_project_modules: Select project modules
323 permission_manage_members: Manage members
326 permission_manage_members: Manage members
324 permission_manage_versions: Manage versions
327 permission_manage_versions: Manage versions
325 permission_manage_categories: Manage issue categories
328 permission_manage_categories: Manage issue categories
326 permission_add_issues: Add issues
329 permission_add_issues: Add issues
327 permission_edit_issues: Edit issues
330 permission_edit_issues: Edit issues
328 permission_manage_issue_relations: Manage issue relations
331 permission_manage_issue_relations: Manage issue relations
329 permission_add_issue_notes: Add notes
332 permission_add_issue_notes: Add notes
330 permission_edit_issue_notes: Edit notes
333 permission_edit_issue_notes: Edit notes
331 permission_edit_own_issue_notes: Edit own notes
334 permission_edit_own_issue_notes: Edit own notes
332 permission_move_issues: Move issues
335 permission_move_issues: Move issues
333 permission_delete_issues: Delete issues
336 permission_delete_issues: Delete issues
334 permission_manage_public_queries: Manage public queries
337 permission_manage_public_queries: Manage public queries
335 permission_save_queries: Save queries
338 permission_save_queries: Save queries
336 permission_view_gantt: View gantt chart
339 permission_view_gantt: View gantt chart
337 permission_view_calendar: View calendar
340 permission_view_calendar: View calendar
338 permission_view_issue_watchers: View watchers list
341 permission_view_issue_watchers: View watchers list
339 permission_add_issue_watchers: Add watchers
342 permission_add_issue_watchers: Add watchers
340 permission_delete_issue_watchers: Delete watchers
343 permission_delete_issue_watchers: Delete watchers
341 permission_log_time: Log spent time
344 permission_log_time: Log spent time
342 permission_view_time_entries: View spent time
345 permission_view_time_entries: View spent time
343 permission_edit_time_entries: Edit time logs
346 permission_edit_time_entries: Edit time logs
344 permission_edit_own_time_entries: Edit own time logs
347 permission_edit_own_time_entries: Edit own time logs
345 permission_manage_news: Manage news
348 permission_manage_news: Manage news
346 permission_comment_news: Comment news
349 permission_comment_news: Comment news
347 permission_manage_documents: Manage documents
350 permission_manage_documents: Manage documents
348 permission_view_documents: View documents
351 permission_view_documents: View documents
349 permission_manage_files: Manage files
352 permission_manage_files: Manage files
350 permission_view_files: View files
353 permission_view_files: View files
351 permission_manage_wiki: Manage wiki
354 permission_manage_wiki: Manage wiki
352 permission_rename_wiki_pages: Rename wiki pages
355 permission_rename_wiki_pages: Rename wiki pages
353 permission_delete_wiki_pages: Delete wiki pages
356 permission_delete_wiki_pages: Delete wiki pages
354 permission_view_wiki_pages: View wiki
357 permission_view_wiki_pages: View wiki
355 permission_view_wiki_edits: View wiki history
358 permission_view_wiki_edits: View wiki history
356 permission_edit_wiki_pages: Edit wiki pages
359 permission_edit_wiki_pages: Edit wiki pages
357 permission_delete_wiki_pages_attachments: Delete attachments
360 permission_delete_wiki_pages_attachments: Delete attachments
358 permission_protect_wiki_pages: Protect wiki pages
361 permission_protect_wiki_pages: Protect wiki pages
359 permission_manage_repository: Manage repository
362 permission_manage_repository: Manage repository
360 permission_browse_repository: Browse repository
363 permission_browse_repository: Browse repository
361 permission_view_changesets: View changesets
364 permission_view_changesets: View changesets
362 permission_commit_access: Commit access
365 permission_commit_access: Commit access
363 permission_manage_boards: Manage boards
366 permission_manage_boards: Manage boards
364 permission_view_messages: View messages
367 permission_view_messages: View messages
365 permission_add_messages: Post messages
368 permission_add_messages: Post messages
366 permission_edit_messages: Edit messages
369 permission_edit_messages: Edit messages
367 permission_edit_own_messages: Edit own messages
370 permission_edit_own_messages: Edit own messages
368 permission_delete_messages: Delete messages
371 permission_delete_messages: Delete messages
369 permission_delete_own_messages: Delete own messages
372 permission_delete_own_messages: Delete own messages
370
373
371 project_module_issue_tracking: Issue tracking
374 project_module_issue_tracking: Issue tracking
372 project_module_time_tracking: Time tracking
375 project_module_time_tracking: Time tracking
373 project_module_news: News
376 project_module_news: News
374 project_module_documents: Documents
377 project_module_documents: Documents
375 project_module_files: Files
378 project_module_files: Files
376 project_module_wiki: Wiki
379 project_module_wiki: Wiki
377 project_module_repository: Repository
380 project_module_repository: Repository
378 project_module_boards: Boards
381 project_module_boards: Boards
379
382
380 label_user: User
383 label_user: User
381 label_user_plural: Users
384 label_user_plural: Users
382 label_user_new: New user
385 label_user_new: New user
383 label_user_anonymous: Anonymous
386 label_user_anonymous: Anonymous
384 label_project: Project
387 label_project: Project
385 label_project_new: New project
388 label_project_new: New project
386 label_project_plural: Projects
389 label_project_plural: Projects
387 label_x_projects:
390 label_x_projects:
388 zero: no projects
391 zero: no projects
389 one: 1 project
392 one: 1 project
390 other: "{{count}} projects"
393 other: "{{count}} projects"
391 label_project_all: All Projects
394 label_project_all: All Projects
392 label_project_latest: Latest projects
395 label_project_latest: Latest projects
393 label_issue: Issue
396 label_issue: Issue
394 label_issue_new: New issue
397 label_issue_new: New issue
395 label_issue_plural: Issues
398 label_issue_plural: Issues
396 label_issue_view_all: View all issues
399 label_issue_view_all: View all issues
397 label_issues_by: "Issues by {{value}}"
400 label_issues_by: "Issues by {{value}}"
398 label_issue_added: Issue added
401 label_issue_added: Issue added
399 label_issue_updated: Issue updated
402 label_issue_updated: Issue updated
400 label_document: Document
403 label_document: Document
401 label_document_new: New document
404 label_document_new: New document
402 label_document_plural: Documents
405 label_document_plural: Documents
403 label_document_added: Document added
406 label_document_added: Document added
404 label_role: Role
407 label_role: Role
405 label_role_plural: Roles
408 label_role_plural: Roles
406 label_role_new: New role
409 label_role_new: New role
407 label_role_and_permissions: Roles and permissions
410 label_role_and_permissions: Roles and permissions
408 label_member: Member
411 label_member: Member
409 label_member_new: New member
412 label_member_new: New member
410 label_member_plural: Members
413 label_member_plural: Members
411 label_tracker: Tracker
414 label_tracker: Tracker
412 label_tracker_plural: Trackers
415 label_tracker_plural: Trackers
413 label_tracker_new: New tracker
416 label_tracker_new: New tracker
414 label_workflow: Workflow
417 label_workflow: Workflow
415 label_issue_status: Issue status
418 label_issue_status: Issue status
416 label_issue_status_plural: Issue statuses
419 label_issue_status_plural: Issue statuses
417 label_issue_status_new: New status
420 label_issue_status_new: New status
418 label_issue_category: Issue category
421 label_issue_category: Issue category
419 label_issue_category_plural: Issue categories
422 label_issue_category_plural: Issue categories
420 label_issue_category_new: New category
423 label_issue_category_new: New category
421 label_custom_field: Custom field
424 label_custom_field: Custom field
422 label_custom_field_plural: Custom fields
425 label_custom_field_plural: Custom fields
423 label_custom_field_new: New custom field
426 label_custom_field_new: New custom field
424 label_enumerations: Enumerations
427 label_enumerations: Enumerations
425 label_enumeration_new: New value
428 label_enumeration_new: New value
426 label_information: Information
429 label_information: Information
427 label_information_plural: Information
430 label_information_plural: Information
428 label_please_login: Please log in
431 label_please_login: Please log in
429 label_register: Register
432 label_register: Register
430 label_login_with_open_id_option: or login with OpenID
433 label_login_with_open_id_option: or login with OpenID
431 label_password_lost: Lost password
434 label_password_lost: Lost password
432 label_home: Home
435 label_home: Home
433 label_my_page: My page
436 label_my_page: My page
434 label_my_account: My account
437 label_my_account: My account
435 label_my_projects: My projects
438 label_my_projects: My projects
436 label_administration: Administration
439 label_administration: Administration
437 label_login: Sign in
440 label_login: Sign in
438 label_logout: Sign out
441 label_logout: Sign out
439 label_help: Help
442 label_help: Help
440 label_reported_issues: Reported issues
443 label_reported_issues: Reported issues
441 label_assigned_to_me_issues: Issues assigned to me
444 label_assigned_to_me_issues: Issues assigned to me
442 label_last_login: Last connection
445 label_last_login: Last connection
443 label_registered_on: Registered on
446 label_registered_on: Registered on
444 label_activity: Activity
447 label_activity: Activity
445 label_overall_activity: Overall activity
448 label_overall_activity: Overall activity
446 label_user_activity: "{{value}}'s activity"
449 label_user_activity: "{{value}}'s activity"
447 label_new: New
450 label_new: New
448 label_logged_as: Logged in as
451 label_logged_as: Logged in as
449 label_environment: Environment
452 label_environment: Environment
450 label_authentication: Authentication
453 label_authentication: Authentication
451 label_auth_source: Authentication mode
454 label_auth_source: Authentication mode
452 label_auth_source_new: New authentication mode
455 label_auth_source_new: New authentication mode
453 label_auth_source_plural: Authentication modes
456 label_auth_source_plural: Authentication modes
454 label_subproject_plural: Subprojects
457 label_subproject_plural: Subprojects
455 label_and_its_subprojects: "{{value}} and its subprojects"
458 label_and_its_subprojects: "{{value}} and its subprojects"
456 label_min_max_length: Min - Max length
459 label_min_max_length: Min - Max length
457 label_list: List
460 label_list: List
458 label_date: Date
461 label_date: Date
459 label_integer: Integer
462 label_integer: Integer
460 label_float: Float
463 label_float: Float
461 label_boolean: Boolean
464 label_boolean: Boolean
462 label_string: Text
465 label_string: Text
463 label_text: Long text
466 label_text: Long text
464 label_attribute: Attribute
467 label_attribute: Attribute
465 label_attribute_plural: Attributes
468 label_attribute_plural: Attributes
466 label_download: "{{count}} Download"
469 label_download: "{{count}} Download"
467 label_download_plural: "{{count}} Downloads"
470 label_download_plural: "{{count}} Downloads"
468 label_no_data: No data to display
471 label_no_data: No data to display
469 label_change_status: Change status
472 label_change_status: Change status
470 label_history: History
473 label_history: History
471 label_attachment: File
474 label_attachment: File
472 label_attachment_new: New file
475 label_attachment_new: New file
473 label_attachment_delete: Delete file
476 label_attachment_delete: Delete file
474 label_attachment_plural: Files
477 label_attachment_plural: Files
475 label_file_added: File added
478 label_file_added: File added
476 label_report: Report
479 label_report: Report
477 label_report_plural: Reports
480 label_report_plural: Reports
478 label_news: News
481 label_news: News
479 label_news_new: Add news
482 label_news_new: Add news
480 label_news_plural: News
483 label_news_plural: News
481 label_news_latest: Latest news
484 label_news_latest: Latest news
482 label_news_view_all: View all news
485 label_news_view_all: View all news
483 label_news_added: News added
486 label_news_added: News added
484 label_change_log: Change log
487 label_change_log: Change log
485 label_settings: Settings
488 label_settings: Settings
486 label_overview: Overview
489 label_overview: Overview
487 label_version: Version
490 label_version: Version
488 label_version_new: New version
491 label_version_new: New version
489 label_version_plural: Versions
492 label_version_plural: Versions
490 label_confirmation: Confirmation
493 label_confirmation: Confirmation
491 label_export_to: 'Also available in:'
494 label_export_to: 'Also available in:'
492 label_read: Read...
495 label_read: Read...
493 label_public_projects: Public projects
496 label_public_projects: Public projects
494 label_open_issues: open
497 label_open_issues: open
495 label_open_issues_plural: open
498 label_open_issues_plural: open
496 label_closed_issues: closed
499 label_closed_issues: closed
497 label_closed_issues_plural: closed
500 label_closed_issues_plural: closed
498 label_x_open_issues_abbr_on_total:
501 label_x_open_issues_abbr_on_total:
499 zero: 0 open / {{total}}
502 zero: 0 open / {{total}}
500 one: 1 open / {{total}}
503 one: 1 open / {{total}}
501 other: "{{count}} open / {{total}}"
504 other: "{{count}} open / {{total}}"
502 label_x_open_issues_abbr:
505 label_x_open_issues_abbr:
503 zero: 0 open
506 zero: 0 open
504 one: 1 open
507 one: 1 open
505 other: "{{count}} open"
508 other: "{{count}} open"
506 label_x_closed_issues_abbr:
509 label_x_closed_issues_abbr:
507 zero: 0 closed
510 zero: 0 closed
508 one: 1 closed
511 one: 1 closed
509 other: "{{count}} closed"
512 other: "{{count}} closed"
510 label_total: Total
513 label_total: Total
511 label_permissions: Permissions
514 label_permissions: Permissions
512 label_current_status: Current status
515 label_current_status: Current status
513 label_new_statuses_allowed: New statuses allowed
516 label_new_statuses_allowed: New statuses allowed
514 label_all: all
517 label_all: all
515 label_none: none
518 label_none: none
516 label_nobody: nobody
519 label_nobody: nobody
517 label_next: Next
520 label_next: Next
518 label_previous: Previous
521 label_previous: Previous
519 label_used_by: Used by
522 label_used_by: Used by
520 label_details: Details
523 label_details: Details
521 label_add_note: Add a note
524 label_add_note: Add a note
522 label_per_page: Per page
525 label_per_page: Per page
523 label_calendar: Calendar
526 label_calendar: Calendar
524 label_months_from: months from
527 label_months_from: months from
525 label_gantt: Gantt
528 label_gantt: Gantt
526 label_internal: Internal
529 label_internal: Internal
527 label_last_changes: "last {{count}} changes"
530 label_last_changes: "last {{count}} changes"
528 label_change_view_all: View all changes
531 label_change_view_all: View all changes
529 label_personalize_page: Personalize this page
532 label_personalize_page: Personalize this page
530 label_comment: Comment
533 label_comment: Comment
531 label_comment_plural: Comments
534 label_comment_plural: Comments
532 label_x_comments:
535 label_x_comments:
533 zero: no comments
536 zero: no comments
534 one: 1 comment
537 one: 1 comment
535 other: "{{count}} comments"
538 other: "{{count}} comments"
536 label_comment_add: Add a comment
539 label_comment_add: Add a comment
537 label_comment_added: Comment added
540 label_comment_added: Comment added
538 label_comment_delete: Delete comments
541 label_comment_delete: Delete comments
539 label_query: Custom query
542 label_query: Custom query
540 label_query_plural: Custom queries
543 label_query_plural: Custom queries
541 label_query_new: New query
544 label_query_new: New query
542 label_filter_add: Add filter
545 label_filter_add: Add filter
543 label_filter_plural: Filters
546 label_filter_plural: Filters
544 label_equals: is
547 label_equals: is
545 label_not_equals: is not
548 label_not_equals: is not
546 label_in_less_than: in less than
549 label_in_less_than: in less than
547 label_in_more_than: in more than
550 label_in_more_than: in more than
548 label_greater_or_equal: '>='
551 label_greater_or_equal: '>='
549 label_less_or_equal: '<='
552 label_less_or_equal: '<='
550 label_in: in
553 label_in: in
551 label_today: today
554 label_today: today
552 label_all_time: all time
555 label_all_time: all time
553 label_yesterday: yesterday
556 label_yesterday: yesterday
554 label_this_week: this week
557 label_this_week: this week
555 label_last_week: last week
558 label_last_week: last week
556 label_last_n_days: "last {{count}} days"
559 label_last_n_days: "last {{count}} days"
557 label_this_month: this month
560 label_this_month: this month
558 label_last_month: last month
561 label_last_month: last month
559 label_this_year: this year
562 label_this_year: this year
560 label_date_range: Date range
563 label_date_range: Date range
561 label_less_than_ago: less than days ago
564 label_less_than_ago: less than days ago
562 label_more_than_ago: more than days ago
565 label_more_than_ago: more than days ago
563 label_ago: days ago
566 label_ago: days ago
564 label_contains: contains
567 label_contains: contains
565 label_not_contains: doesn't contain
568 label_not_contains: doesn't contain
566 label_day_plural: days
569 label_day_plural: days
567 label_repository: Repository
570 label_repository: Repository
568 label_repository_plural: Repositories
571 label_repository_plural: Repositories
569 label_browse: Browse
572 label_browse: Browse
570 label_modification: "{{count}} change"
573 label_modification: "{{count}} change"
571 label_modification_plural: "{{count}} changes"
574 label_modification_plural: "{{count}} changes"
572 label_branch: Branch
575 label_branch: Branch
573 label_tag: Tag
576 label_tag: Tag
574 label_revision: Revision
577 label_revision: Revision
575 label_revision_plural: Revisions
578 label_revision_plural: Revisions
576 label_associated_revisions: Associated revisions
579 label_associated_revisions: Associated revisions
577 label_added: added
580 label_added: added
578 label_modified: modified
581 label_modified: modified
579 label_copied: copied
582 label_copied: copied
580 label_renamed: renamed
583 label_renamed: renamed
581 label_deleted: deleted
584 label_deleted: deleted
582 label_latest_revision: Latest revision
585 label_latest_revision: Latest revision
583 label_latest_revision_plural: Latest revisions
586 label_latest_revision_plural: Latest revisions
584 label_view_revisions: View revisions
587 label_view_revisions: View revisions
585 label_view_all_revisions: View all revisions
588 label_view_all_revisions: View all revisions
586 label_max_size: Maximum size
589 label_max_size: Maximum size
587 label_sort_highest: Move to top
590 label_sort_highest: Move to top
588 label_sort_higher: Move up
591 label_sort_higher: Move up
589 label_sort_lower: Move down
592 label_sort_lower: Move down
590 label_sort_lowest: Move to bottom
593 label_sort_lowest: Move to bottom
591 label_roadmap: Roadmap
594 label_roadmap: Roadmap
592 label_roadmap_due_in: "Due in {{value}}"
595 label_roadmap_due_in: "Due in {{value}}"
593 label_roadmap_overdue: "{{value}} late"
596 label_roadmap_overdue: "{{value}} late"
594 label_roadmap_no_issues: No issues for this version
597 label_roadmap_no_issues: No issues for this version
595 label_search: Search
598 label_search: Search
596 label_result_plural: Results
599 label_result_plural: Results
597 label_all_words: All words
600 label_all_words: All words
598 label_wiki: Wiki
601 label_wiki: Wiki
599 label_wiki_edit: Wiki edit
602 label_wiki_edit: Wiki edit
600 label_wiki_edit_plural: Wiki edits
603 label_wiki_edit_plural: Wiki edits
601 label_wiki_page: Wiki page
604 label_wiki_page: Wiki page
602 label_wiki_page_plural: Wiki pages
605 label_wiki_page_plural: Wiki pages
603 label_index_by_title: Index by title
606 label_index_by_title: Index by title
604 label_index_by_date: Index by date
607 label_index_by_date: Index by date
605 label_current_version: Current version
608 label_current_version: Current version
606 label_preview: Preview
609 label_preview: Preview
607 label_feed_plural: Feeds
610 label_feed_plural: Feeds
608 label_changes_details: Details of all changes
611 label_changes_details: Details of all changes
609 label_issue_tracking: Issue tracking
612 label_issue_tracking: Issue tracking
610 label_spent_time: Spent time
613 label_spent_time: Spent time
611 label_f_hour: "{{value}} hour"
614 label_f_hour: "{{value}} hour"
612 label_f_hour_plural: "{{value}} hours"
615 label_f_hour_plural: "{{value}} hours"
613 label_time_tracking: Time tracking
616 label_time_tracking: Time tracking
614 label_change_plural: Changes
617 label_change_plural: Changes
615 label_statistics: Statistics
618 label_statistics: Statistics
616 label_commits_per_month: Commits per month
619 label_commits_per_month: Commits per month
617 label_commits_per_author: Commits per author
620 label_commits_per_author: Commits per author
618 label_view_diff: View differences
621 label_view_diff: View differences
619 label_diff_inline: inline
622 label_diff_inline: inline
620 label_diff_side_by_side: side by side
623 label_diff_side_by_side: side by side
621 label_options: Options
624 label_options: Options
622 label_copy_workflow_from: Copy workflow from
625 label_copy_workflow_from: Copy workflow from
623 label_permissions_report: Permissions report
626 label_permissions_report: Permissions report
624 label_watched_issues: Watched issues
627 label_watched_issues: Watched issues
625 label_related_issues: Related issues
628 label_related_issues: Related issues
626 label_applied_status: Applied status
629 label_applied_status: Applied status
627 label_loading: Loading...
630 label_loading: Loading...
628 label_relation_new: New relation
631 label_relation_new: New relation
629 label_relation_delete: Delete relation
632 label_relation_delete: Delete relation
630 label_relates_to: related to
633 label_relates_to: related to
631 label_duplicates: duplicates
634 label_duplicates: duplicates
632 label_duplicated_by: duplicated by
635 label_duplicated_by: duplicated by
633 label_blocks: blocks
636 label_blocks: blocks
634 label_blocked_by: blocked by
637 label_blocked_by: blocked by
635 label_precedes: precedes
638 label_precedes: precedes
636 label_follows: follows
639 label_follows: follows
637 label_end_to_start: end to start
640 label_end_to_start: end to start
638 label_end_to_end: end to end
641 label_end_to_end: end to end
639 label_start_to_start: start to start
642 label_start_to_start: start to start
640 label_start_to_end: start to end
643 label_start_to_end: start to end
641 label_stay_logged_in: Stay logged in
644 label_stay_logged_in: Stay logged in
642 label_disabled: disabled
645 label_disabled: disabled
643 label_show_completed_versions: Show completed versions
646 label_show_completed_versions: Show completed versions
644 label_me: me
647 label_me: me
645 label_board: Forum
648 label_board: Forum
646 label_board_new: New forum
649 label_board_new: New forum
647 label_board_plural: Forums
650 label_board_plural: Forums
648 label_topic_plural: Topics
651 label_topic_plural: Topics
649 label_message_plural: Messages
652 label_message_plural: Messages
650 label_message_last: Last message
653 label_message_last: Last message
651 label_message_new: New message
654 label_message_new: New message
652 label_message_posted: Message added
655 label_message_posted: Message added
653 label_reply_plural: Replies
656 label_reply_plural: Replies
654 label_send_information: Send account information to the user
657 label_send_information: Send account information to the user
655 label_year: Year
658 label_year: Year
656 label_month: Month
659 label_month: Month
657 label_week: Week
660 label_week: Week
658 label_date_from: From
661 label_date_from: From
659 label_date_to: To
662 label_date_to: To
660 label_language_based: Based on user's language
663 label_language_based: Based on user's language
661 label_sort_by: "Sort by {{value}}"
664 label_sort_by: "Sort by {{value}}"
662 label_send_test_email: Send a test email
665 label_send_test_email: Send a test email
663 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
666 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
664 label_module_plural: Modules
667 label_module_plural: Modules
665 label_added_time_by: "Added by {{author}} {{age}} ago"
668 label_added_time_by: "Added by {{author}} {{age}} ago"
666 label_updated_time_by: "Updated by {{author}} {{age}} ago"
669 label_updated_time_by: "Updated by {{author}} {{age}} ago"
667 label_updated_time: "Updated {{value}} ago"
670 label_updated_time: "Updated {{value}} ago"
668 label_jump_to_a_project: Jump to a project...
671 label_jump_to_a_project: Jump to a project...
669 label_file_plural: Files
672 label_file_plural: Files
670 label_changeset_plural: Changesets
673 label_changeset_plural: Changesets
671 label_default_columns: Default columns
674 label_default_columns: Default columns
672 label_no_change_option: (No change)
675 label_no_change_option: (No change)
673 label_bulk_edit_selected_issues: Bulk edit selected issues
676 label_bulk_edit_selected_issues: Bulk edit selected issues
674 label_theme: Theme
677 label_theme: Theme
675 label_default: Default
678 label_default: Default
676 label_search_titles_only: Search titles only
679 label_search_titles_only: Search titles only
677 label_user_mail_option_all: "For any event on all my projects"
680 label_user_mail_option_all: "For any event on all my projects"
678 label_user_mail_option_selected: "For any event on the selected projects only..."
681 label_user_mail_option_selected: "For any event on the selected projects only..."
679 label_user_mail_option_none: "Only for things I watch or I'm involved in"
682 label_user_mail_option_none: "Only for things I watch or I'm involved in"
680 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
683 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
681 label_registration_activation_by_email: account activation by email
684 label_registration_activation_by_email: account activation by email
682 label_registration_manual_activation: manual account activation
685 label_registration_manual_activation: manual account activation
683 label_registration_automatic_activation: automatic account activation
686 label_registration_automatic_activation: automatic account activation
684 label_display_per_page: "Per page: {{value}}"
687 label_display_per_page: "Per page: {{value}}"
685 label_age: Age
688 label_age: Age
686 label_change_properties: Change properties
689 label_change_properties: Change properties
687 label_general: General
690 label_general: General
688 label_more: More
691 label_more: More
689 label_scm: SCM
692 label_scm: SCM
690 label_plugins: Plugins
693 label_plugins: Plugins
691 label_ldap_authentication: LDAP authentication
694 label_ldap_authentication: LDAP authentication
692 label_downloads_abbr: D/L
695 label_downloads_abbr: D/L
693 label_optional_description: Optional description
696 label_optional_description: Optional description
694 label_add_another_file: Add another file
697 label_add_another_file: Add another file
695 label_preferences: Preferences
698 label_preferences: Preferences
696 label_chronological_order: In chronological order
699 label_chronological_order: In chronological order
697 label_reverse_chronological_order: In reverse chronological order
700 label_reverse_chronological_order: In reverse chronological order
698 label_planning: Planning
701 label_planning: Planning
699 label_incoming_emails: Incoming emails
702 label_incoming_emails: Incoming emails
700 label_generate_key: Generate a key
703 label_generate_key: Generate a key
701 label_issue_watchers: Watchers
704 label_issue_watchers: Watchers
702 label_example: Example
705 label_example: Example
703 label_display: Display
706 label_display: Display
704 label_sort: Sort
707 label_sort: Sort
705 label_ascending: Ascending
708 label_ascending: Ascending
706 label_descending: Descending
709 label_descending: Descending
707 label_date_from_to: From {{start}} to {{end}}
710 label_date_from_to: From {{start}} to {{end}}
708 label_wiki_content_added: Wiki page added
711 label_wiki_content_added: Wiki page added
709 label_wiki_content_updated: Wiki page updated
712 label_wiki_content_updated: Wiki page updated
710 label_group: Group
713 label_group: Group
711 label_group_plural: Groups
714 label_group_plural: Groups
712 label_group_new: New group
715 label_group_new: New group
713 label_time_entry_plural: Spent time
716 label_time_entry_plural: Spent time
714 label_version_sharing_none: Not shared
717 label_version_sharing_none: Not shared
715 label_version_sharing_descendants: With subprojects
718 label_version_sharing_descendants: With subprojects
716 label_version_sharing_hierarchy: With project hierarchy
719 label_version_sharing_hierarchy: With project hierarchy
717 label_version_sharing_tree: With project tree
720 label_version_sharing_tree: With project tree
718 label_version_sharing_system: With all projects
721 label_version_sharing_system: With all projects
722 label_update_issue_done_ratios: Update issue done ratios
719
723
720 button_login: Login
724 button_login: Login
721 button_submit: Submit
725 button_submit: Submit
722 button_save: Save
726 button_save: Save
723 button_check_all: Check all
727 button_check_all: Check all
724 button_uncheck_all: Uncheck all
728 button_uncheck_all: Uncheck all
725 button_delete: Delete
729 button_delete: Delete
726 button_create: Create
730 button_create: Create
727 button_create_and_continue: Create and continue
731 button_create_and_continue: Create and continue
728 button_test: Test
732 button_test: Test
729 button_edit: Edit
733 button_edit: Edit
730 button_add: Add
734 button_add: Add
731 button_change: Change
735 button_change: Change
732 button_apply: Apply
736 button_apply: Apply
733 button_clear: Clear
737 button_clear: Clear
734 button_lock: Lock
738 button_lock: Lock
735 button_unlock: Unlock
739 button_unlock: Unlock
736 button_download: Download
740 button_download: Download
737 button_list: List
741 button_list: List
738 button_view: View
742 button_view: View
739 button_move: Move
743 button_move: Move
740 button_move_and_follow: Move and follow
744 button_move_and_follow: Move and follow
741 button_back: Back
745 button_back: Back
742 button_cancel: Cancel
746 button_cancel: Cancel
743 button_activate: Activate
747 button_activate: Activate
744 button_sort: Sort
748 button_sort: Sort
745 button_log_time: Log time
749 button_log_time: Log time
746 button_rollback: Rollback to this version
750 button_rollback: Rollback to this version
747 button_watch: Watch
751 button_watch: Watch
748 button_unwatch: Unwatch
752 button_unwatch: Unwatch
749 button_reply: Reply
753 button_reply: Reply
750 button_archive: Archive
754 button_archive: Archive
751 button_unarchive: Unarchive
755 button_unarchive: Unarchive
752 button_reset: Reset
756 button_reset: Reset
753 button_rename: Rename
757 button_rename: Rename
754 button_change_password: Change password
758 button_change_password: Change password
755 button_copy: Copy
759 button_copy: Copy
756 button_copy_and_follow: Copy and follow
760 button_copy_and_follow: Copy and follow
757 button_annotate: Annotate
761 button_annotate: Annotate
758 button_update: Update
762 button_update: Update
759 button_configure: Configure
763 button_configure: Configure
760 button_quote: Quote
764 button_quote: Quote
761 button_duplicate: Duplicate
765 button_duplicate: Duplicate
762
766
763 status_active: active
767 status_active: active
764 status_registered: registered
768 status_registered: registered
765 status_locked: locked
769 status_locked: locked
766
770
767 version_status_open: open
771 version_status_open: open
768 version_status_locked: locked
772 version_status_locked: locked
769 version_status_closed: closed
773 version_status_closed: closed
770
774
771 field_active: Active
775 field_active: Active
772
776
773 text_select_mail_notifications: Select actions for which email notifications should be sent.
777 text_select_mail_notifications: Select actions for which email notifications should be sent.
774 text_regexp_info: eg. ^[A-Z0-9]+$
778 text_regexp_info: eg. ^[A-Z0-9]+$
775 text_min_max_length_info: 0 means no restriction
779 text_min_max_length_info: 0 means no restriction
776 text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
780 text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
777 text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
781 text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
778 text_workflow_edit: Select a role and a tracker to edit the workflow
782 text_workflow_edit: Select a role and a tracker to edit the workflow
779 text_are_you_sure: Are you sure ?
783 text_are_you_sure: Are you sure ?
780 text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
784 text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
781 text_journal_set_to: "{{label}} set to {{value}}"
785 text_journal_set_to: "{{label}} set to {{value}}"
782 text_journal_deleted: "{{label}} deleted ({{old}})"
786 text_journal_deleted: "{{label}} deleted ({{old}})"
783 text_journal_added: "{{label}} {{value}} added"
787 text_journal_added: "{{label}} {{value}} added"
784 text_tip_task_begin_day: task beginning this day
788 text_tip_task_begin_day: task beginning this day
785 text_tip_task_end_day: task ending this day
789 text_tip_task_end_day: task ending this day
786 text_tip_task_begin_end_day: task beginning and ending this day
790 text_tip_task_begin_end_day: task beginning and ending this day
787 text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br />Once saved, the identifier can not be changed.'
791 text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br />Once saved, the identifier can not be changed.'
788 text_caracters_maximum: "{{count}} characters maximum."
792 text_caracters_maximum: "{{count}} characters maximum."
789 text_caracters_minimum: "Must be at least {{count}} characters long."
793 text_caracters_minimum: "Must be at least {{count}} characters long."
790 text_length_between: "Length between {{min}} and {{max}} characters."
794 text_length_between: "Length between {{min}} and {{max}} characters."
791 text_tracker_no_workflow: No workflow defined for this tracker
795 text_tracker_no_workflow: No workflow defined for this tracker
792 text_unallowed_characters: Unallowed characters
796 text_unallowed_characters: Unallowed characters
793 text_comma_separated: Multiple values allowed (comma separated).
797 text_comma_separated: Multiple values allowed (comma separated).
794 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
798 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
795 text_issue_added: "Issue {{id}} has been reported by {{author}}."
799 text_issue_added: "Issue {{id}} has been reported by {{author}}."
796 text_issue_updated: "Issue {{id}} has been updated by {{author}}."
800 text_issue_updated: "Issue {{id}} has been updated by {{author}}."
797 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
801 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
798 text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
802 text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
799 text_issue_category_destroy_assignments: Remove category assignments
803 text_issue_category_destroy_assignments: Remove category assignments
800 text_issue_category_reassign_to: Reassign issues to this category
804 text_issue_category_reassign_to: Reassign issues to this category
801 text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
805 text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
802 text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
806 text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
803 text_load_default_configuration: Load the default configuration
807 text_load_default_configuration: Load the default configuration
804 text_status_changed_by_changeset: "Applied in changeset {{value}}."
808 text_status_changed_by_changeset: "Applied in changeset {{value}}."
805 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
809 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
806 text_select_project_modules: 'Select modules to enable for this project:'
810 text_select_project_modules: 'Select modules to enable for this project:'
807 text_default_administrator_account_changed: Default administrator account changed
811 text_default_administrator_account_changed: Default administrator account changed
808 text_file_repository_writable: Attachments directory writable
812 text_file_repository_writable: Attachments directory writable
809 text_plugin_assets_writable: Plugin assets directory writable
813 text_plugin_assets_writable: Plugin assets directory writable
810 text_rmagick_available: RMagick available (optional)
814 text_rmagick_available: RMagick available (optional)
811 text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
815 text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
812 text_destroy_time_entries: Delete reported hours
816 text_destroy_time_entries: Delete reported hours
813 text_assign_time_entries_to_project: Assign reported hours to the project
817 text_assign_time_entries_to_project: Assign reported hours to the project
814 text_reassign_time_entries: 'Reassign reported hours to this issue:'
818 text_reassign_time_entries: 'Reassign reported hours to this issue:'
815 text_user_wrote: "{{value}} wrote:"
819 text_user_wrote: "{{value}} wrote:"
816 text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
820 text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
817 text_enumeration_category_reassign_to: 'Reassign them to this value:'
821 text_enumeration_category_reassign_to: 'Reassign them to this value:'
818 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
822 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
819 text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
823 text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
820 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
824 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
821 text_custom_field_possible_values_info: 'One line for each value'
825 text_custom_field_possible_values_info: 'One line for each value'
822 text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
826 text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
823 text_wiki_page_nullify_children: "Keep child pages as root pages"
827 text_wiki_page_nullify_children: "Keep child pages as root pages"
824 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
828 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
825 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
829 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
826
830
827 default_role_manager: Manager
831 default_role_manager: Manager
828 default_role_developper: Developer
832 default_role_developper: Developer
829 default_role_reporter: Reporter
833 default_role_reporter: Reporter
830 default_tracker_bug: Bug
834 default_tracker_bug: Bug
831 default_tracker_feature: Feature
835 default_tracker_feature: Feature
832 default_tracker_support: Support
836 default_tracker_support: Support
833 default_issue_status_new: New
837 default_issue_status_new: New
834 default_issue_status_in_progress: In Progress
838 default_issue_status_in_progress: In Progress
835 default_issue_status_resolved: Resolved
839 default_issue_status_resolved: Resolved
836 default_issue_status_feedback: Feedback
840 default_issue_status_feedback: Feedback
837 default_issue_status_closed: Closed
841 default_issue_status_closed: Closed
838 default_issue_status_rejected: Rejected
842 default_issue_status_rejected: Rejected
839 default_doc_category_user: User documentation
843 default_doc_category_user: User documentation
840 default_doc_category_tech: Technical documentation
844 default_doc_category_tech: Technical documentation
841 default_priority_low: Low
845 default_priority_low: Low
842 default_priority_normal: Normal
846 default_priority_normal: Normal
843 default_priority_high: High
847 default_priority_high: High
844 default_priority_urgent: Urgent
848 default_priority_urgent: Urgent
845 default_priority_immediate: Immediate
849 default_priority_immediate: Immediate
846 default_activity_design: Design
850 default_activity_design: Design
847 default_activity_development: Development
851 default_activity_development: Development
848
852
849 enumeration_issue_priorities: Issue priorities
853 enumeration_issue_priorities: Issue priorities
850 enumeration_doc_categories: Document categories
854 enumeration_doc_categories: Document categories
851 enumeration_activities: Activities (time tracking)
855 enumeration_activities: Activities (time tracking)
852 enumeration_system_activity: System Activity
856 enumeration_system_activity: System Activity
857
858 issue_field: Use the issue field
859 issue_status: Use the issue status
@@ -1,172 +1,174
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18
18
19 # DO NOT MODIFY THIS FILE !!!
19 # DO NOT MODIFY THIS FILE !!!
20 # Settings can be defined through the application in Admin -> Settings
20 # Settings can be defined through the application in Admin -> Settings
21
21
22 app_title:
22 app_title:
23 default: Redmine
23 default: Redmine
24 app_subtitle:
24 app_subtitle:
25 default: Project management
25 default: Project management
26 welcome_text:
26 welcome_text:
27 default:
27 default:
28 login_required:
28 login_required:
29 default: 0
29 default: 0
30 self_registration:
30 self_registration:
31 default: '2'
31 default: '2'
32 lost_password:
32 lost_password:
33 default: 1
33 default: 1
34 password_min_length:
34 password_min_length:
35 format: int
35 format: int
36 default: 4
36 default: 4
37 attachment_max_size:
37 attachment_max_size:
38 format: int
38 format: int
39 default: 5120
39 default: 5120
40 issues_export_limit:
40 issues_export_limit:
41 format: int
41 format: int
42 default: 500
42 default: 500
43 activity_days_default:
43 activity_days_default:
44 format: int
44 format: int
45 default: 30
45 default: 30
46 per_page_options:
46 per_page_options:
47 default: '25,50,100'
47 default: '25,50,100'
48 mail_from:
48 mail_from:
49 default: redmine@example.net
49 default: redmine@example.net
50 bcc_recipients:
50 bcc_recipients:
51 default: 1
51 default: 1
52 plain_text_mail:
52 plain_text_mail:
53 default: 0
53 default: 0
54 text_formatting:
54 text_formatting:
55 default: textile
55 default: textile
56 wiki_compression:
56 wiki_compression:
57 default: ""
57 default: ""
58 default_language:
58 default_language:
59 default: en
59 default: en
60 host_name:
60 host_name:
61 default: localhost:3000
61 default: localhost:3000
62 protocol:
62 protocol:
63 default: http
63 default: http
64 feeds_limit:
64 feeds_limit:
65 format: int
65 format: int
66 default: 15
66 default: 15
67 # Maximum size of files that can be displayed
67 # Maximum size of files that can be displayed
68 # inline through the file viewer (in KB)
68 # inline through the file viewer (in KB)
69 file_max_size_displayed:
69 file_max_size_displayed:
70 format: int
70 format: int
71 default: 512
71 default: 512
72 diff_max_lines_displayed:
72 diff_max_lines_displayed:
73 format: int
73 format: int
74 default: 1500
74 default: 1500
75 enabled_scm:
75 enabled_scm:
76 serialized: true
76 serialized: true
77 default:
77 default:
78 - Subversion
78 - Subversion
79 - Darcs
79 - Darcs
80 - Mercurial
80 - Mercurial
81 - Cvs
81 - Cvs
82 - Bazaar
82 - Bazaar
83 - Git
83 - Git
84 autofetch_changesets:
84 autofetch_changesets:
85 default: 1
85 default: 1
86 sys_api_enabled:
86 sys_api_enabled:
87 default: 0
87 default: 0
88 commit_ref_keywords:
88 commit_ref_keywords:
89 default: 'refs,references,IssueID'
89 default: 'refs,references,IssueID'
90 commit_fix_keywords:
90 commit_fix_keywords:
91 default: 'fixes,closes'
91 default: 'fixes,closes'
92 commit_fix_status_id:
92 commit_fix_status_id:
93 format: int
93 format: int
94 default: 0
94 default: 0
95 commit_fix_done_ratio:
95 commit_fix_done_ratio:
96 default: 100
96 default: 100
97 # autologin duration in days
97 # autologin duration in days
98 # 0 means autologin is disabled
98 # 0 means autologin is disabled
99 autologin:
99 autologin:
100 format: int
100 format: int
101 default: 0
101 default: 0
102 # date format
102 # date format
103 date_format:
103 date_format:
104 default: ''
104 default: ''
105 time_format:
105 time_format:
106 default: ''
106 default: ''
107 user_format:
107 user_format:
108 default: :firstname_lastname
108 default: :firstname_lastname
109 format: symbol
109 format: symbol
110 cross_project_issue_relations:
110 cross_project_issue_relations:
111 default: 0
111 default: 0
112 notified_events:
112 notified_events:
113 serialized: true
113 serialized: true
114 default:
114 default:
115 - issue_added
115 - issue_added
116 - issue_updated
116 - issue_updated
117 mail_handler_api_enabled:
117 mail_handler_api_enabled:
118 default: 0
118 default: 0
119 mail_handler_api_key:
119 mail_handler_api_key:
120 default:
120 default:
121 issue_list_default_columns:
121 issue_list_default_columns:
122 serialized: true
122 serialized: true
123 default:
123 default:
124 - tracker
124 - tracker
125 - status
125 - status
126 - priority
126 - priority
127 - subject
127 - subject
128 - assigned_to
128 - assigned_to
129 - updated_on
129 - updated_on
130 display_subprojects_issues:
130 display_subprojects_issues:
131 default: 1
131 default: 1
132 issue_done_ratio:
133 default: 'issue_field'
132 default_projects_public:
134 default_projects_public:
133 default: 1
135 default: 1
134 default_projects_modules:
136 default_projects_modules:
135 serialized: true
137 serialized: true
136 default:
138 default:
137 - issue_tracking
139 - issue_tracking
138 - time_tracking
140 - time_tracking
139 - news
141 - news
140 - documents
142 - documents
141 - files
143 - files
142 - wiki
144 - wiki
143 - repository
145 - repository
144 - boards
146 - boards
145 # Role given to a non-admin user who creates a project
147 # Role given to a non-admin user who creates a project
146 new_project_user_role_id:
148 new_project_user_role_id:
147 format: int
149 format: int
148 default: ''
150 default: ''
149 sequential_project_identifiers:
151 sequential_project_identifiers:
150 default: 0
152 default: 0
151 # encodings used to convert repository files content to UTF-8
153 # encodings used to convert repository files content to UTF-8
152 # multiple values accepted, comma separated
154 # multiple values accepted, comma separated
153 repositories_encodings:
155 repositories_encodings:
154 default: ''
156 default: ''
155 # encoding used to convert commit logs to UTF-8
157 # encoding used to convert commit logs to UTF-8
156 commit_logs_encoding:
158 commit_logs_encoding:
157 default: 'UTF-8'
159 default: 'UTF-8'
158 repository_log_display_limit:
160 repository_log_display_limit:
159 format: int
161 format: int
160 default: 100
162 default: 100
161 ui_theme:
163 ui_theme:
162 default: ''
164 default: ''
163 emails_footer:
165 emails_footer:
164 default: |-
166 default: |-
165 You have received this notification because you have either subscribed to it, or are involved in it.
167 You have received this notification because you have either subscribed to it, or are involved in it.
166 To change your notification preferences, please click here: http://hostname/my/account
168 To change your notification preferences, please click here: http://hostname/my/account
167 gravatar_enabled:
169 gravatar_enabled:
168 default: 0
170 default: 0
169 openid:
171 openid:
170 default: 0
172 default: 0
171 gravatar_default:
173 gravatar_default:
172 default: ''
174 default: ''
@@ -1,815 +1,816
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2
2
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 h1 {margin:0; padding:0; font-size: 24px;}
4 h1 {margin:0; padding:0; font-size: 24px;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8
8
9 /***** Layout *****/
9 /***** Layout *****/
10 #wrapper {background: white;}
10 #wrapper {background: white;}
11
11
12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
13 #top-menu ul {margin: 0; padding: 0;}
13 #top-menu ul {margin: 0; padding: 0;}
14 #top-menu li {
14 #top-menu li {
15 float:left;
15 float:left;
16 list-style-type:none;
16 list-style-type:none;
17 margin: 0px 0px 0px 0px;
17 margin: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
19 white-space:nowrap;
19 white-space:nowrap;
20 }
20 }
21 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
21 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
23
23
24 #account {float:right;}
24 #account {float:right;}
25
25
26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
27 #header a {color:#f8f8f8;}
27 #header a {color:#f8f8f8;}
28 #header h1 a.ancestor { font-size: 80%; }
28 #header h1 a.ancestor { font-size: 80%; }
29 #quick-search {float:right;}
29 #quick-search {float:right;}
30
30
31 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
31 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
32 #main-menu ul {margin: 0; padding: 0;}
32 #main-menu ul {margin: 0; padding: 0;}
33 #main-menu li {
33 #main-menu li {
34 float:left;
34 float:left;
35 list-style-type:none;
35 list-style-type:none;
36 margin: 0px 2px 0px 0px;
36 margin: 0px 2px 0px 0px;
37 padding: 0px 0px 0px 0px;
37 padding: 0px 0px 0px 0px;
38 white-space:nowrap;
38 white-space:nowrap;
39 }
39 }
40 #main-menu li a {
40 #main-menu li a {
41 display: block;
41 display: block;
42 color: #fff;
42 color: #fff;
43 text-decoration: none;
43 text-decoration: none;
44 font-weight: bold;
44 font-weight: bold;
45 margin: 0;
45 margin: 0;
46 padding: 4px 10px 4px 10px;
46 padding: 4px 10px 4px 10px;
47 }
47 }
48 #main-menu li a:hover {background:#759FCF; color:#fff;}
48 #main-menu li a:hover {background:#759FCF; color:#fff;}
49 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
49 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
50
50
51 #main {background-color:#EEEEEE;}
51 #main {background-color:#EEEEEE;}
52
52
53 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
53 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
54 * html #sidebar{ width: 17%; }
54 * html #sidebar{ width: 17%; }
55 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
55 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
56 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
56 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
57 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
57 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
58
58
59 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
59 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
60 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
60 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
61 html>body #content { min-height: 600px; }
61 html>body #content { min-height: 600px; }
62 * html body #content { height: 600px; } /* IE */
62 * html body #content { height: 600px; } /* IE */
63
63
64 #main.nosidebar #sidebar{ display: none; }
64 #main.nosidebar #sidebar{ display: none; }
65 #main.nosidebar #content{ width: auto; border-right: 0; }
65 #main.nosidebar #content{ width: auto; border-right: 0; }
66
66
67 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
67 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
68
68
69 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
69 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
70 #login-form table td {padding: 6px;}
70 #login-form table td {padding: 6px;}
71 #login-form label {font-weight: bold;}
71 #login-form label {font-weight: bold;}
72 #login-form input#username, #login-form input#password { width: 300px; }
72 #login-form input#username, #login-form input#password { width: 300px; }
73
73
74 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
74 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
75
75
76 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
76 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
77
77
78 /***** Links *****/
78 /***** Links *****/
79 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
79 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
80 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
80 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
81 a img{ border: 0; }
81 a img{ border: 0; }
82
82
83 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
83 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
84
84
85 /***** Tables *****/
85 /***** Tables *****/
86 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
86 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
87 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
87 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
88 table.list td { vertical-align: top; }
88 table.list td { vertical-align: top; }
89 table.list td.id { width: 2%; text-align: center;}
89 table.list td.id { width: 2%; text-align: center;}
90 table.list td.checkbox { width: 15px; padding: 0px;}
90 table.list td.checkbox { width: 15px; padding: 0px;}
91 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
91 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
92 table.list td.buttons a { padding-right: 0.6em; }
92 table.list td.buttons a { padding-right: 0.6em; }
93
93
94 tr.project td.name a { padding-left: 16px; white-space:nowrap; }
94 tr.project td.name a { padding-left: 16px; white-space:nowrap; }
95 tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; }
95 tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; }
96
96
97 tr.issue { text-align: center; white-space: nowrap; }
97 tr.issue { text-align: center; white-space: nowrap; }
98 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
98 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
99 tr.issue td.subject { text-align: left; }
99 tr.issue td.subject { text-align: left; }
100 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
100 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
101
101
102 tr.entry { border: 1px solid #f8f8f8; }
102 tr.entry { border: 1px solid #f8f8f8; }
103 tr.entry td { white-space: nowrap; }
103 tr.entry td { white-space: nowrap; }
104 tr.entry td.filename { width: 30%; }
104 tr.entry td.filename { width: 30%; }
105 tr.entry td.size { text-align: right; font-size: 90%; }
105 tr.entry td.size { text-align: right; font-size: 90%; }
106 tr.entry td.revision, tr.entry td.author { text-align: center; }
106 tr.entry td.revision, tr.entry td.author { text-align: center; }
107 tr.entry td.age { text-align: right; }
107 tr.entry td.age { text-align: right; }
108 tr.entry.file td.filename a { margin-left: 16px; }
108 tr.entry.file td.filename a { margin-left: 16px; }
109
109
110 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
110 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
111 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
111 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
112
112
113 tr.changeset td.author { text-align: center; width: 15%; }
113 tr.changeset td.author { text-align: center; width: 15%; }
114 tr.changeset td.committed_on { text-align: center; width: 15%; }
114 tr.changeset td.committed_on { text-align: center; width: 15%; }
115
115
116 table.files tr.file td { text-align: center; }
116 table.files tr.file td { text-align: center; }
117 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
117 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
118 table.files tr.file td.digest { font-size: 80%; }
118 table.files tr.file td.digest { font-size: 80%; }
119
119
120 table.members td.roles, table.memberships td.roles { width: 45%; }
120 table.members td.roles, table.memberships td.roles { width: 45%; }
121
121
122 tr.message { height: 2.6em; }
122 tr.message { height: 2.6em; }
123 tr.message td.last_message { font-size: 80%; }
123 tr.message td.last_message { font-size: 80%; }
124 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
124 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
125 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
125 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
126
126
127 tr.version.closed, tr.version.closed a { color: #999; }
127 tr.version.closed, tr.version.closed a { color: #999; }
128 tr.version td.name { padding-left: 20px; }
128 tr.version td.name { padding-left: 20px; }
129 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
129 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
130 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; }
130 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; }
131
131
132 tr.user td { width:13%; }
132 tr.user td { width:13%; }
133 tr.user td.email { width:18%; }
133 tr.user td.email { width:18%; }
134 tr.user td { white-space: nowrap; }
134 tr.user td { white-space: nowrap; }
135 tr.user.locked, tr.user.registered { color: #aaa; }
135 tr.user.locked, tr.user.registered { color: #aaa; }
136 tr.user.locked a, tr.user.registered a { color: #aaa; }
136 tr.user.locked a, tr.user.registered a { color: #aaa; }
137
137
138 tr.time-entry { text-align: center; white-space: nowrap; }
138 tr.time-entry { text-align: center; white-space: nowrap; }
139 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
139 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
140 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
140 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
141 td.hours .hours-dec { font-size: 0.9em; }
141 td.hours .hours-dec { font-size: 0.9em; }
142
142
143 table.plugins td { vertical-align: middle; }
143 table.plugins td { vertical-align: middle; }
144 table.plugins td.configure { text-align: right; padding-right: 1em; }
144 table.plugins td.configure { text-align: right; padding-right: 1em; }
145 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
145 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
146 table.plugins span.description { display: block; font-size: 0.9em; }
146 table.plugins span.description { display: block; font-size: 0.9em; }
147 table.plugins span.url { display: block; font-size: 0.9em; }
147 table.plugins span.url { display: block; font-size: 0.9em; }
148
148
149 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; }
149 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; }
150 table.list tbody tr.group span.count { color: #aaa; font-size: 80%; }
150 table.list tbody tr.group span.count { color: #aaa; font-size: 80%; }
151
151
152 table.list tbody tr:hover { background-color:#ffffdd; }
152 table.list tbody tr:hover { background-color:#ffffdd; }
153 table.list tbody tr.group:hover { background-color:inherit; }
153 table.list tbody tr.group:hover { background-color:inherit; }
154 table td {padding:2px;}
154 table td {padding:2px;}
155 table p {margin:0;}
155 table p {margin:0;}
156 .odd {background-color:#f6f7f8;}
156 .odd {background-color:#f6f7f8;}
157 .even {background-color: #fff;}
157 .even {background-color: #fff;}
158
158
159 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
159 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
160 a.sort.asc { background-image: url(../images/sort_asc.png); }
160 a.sort.asc { background-image: url(../images/sort_asc.png); }
161 a.sort.desc { background-image: url(../images/sort_desc.png); }
161 a.sort.desc { background-image: url(../images/sort_desc.png); }
162
162
163 table.attributes { width: 100% }
163 table.attributes { width: 100% }
164 table.attributes th { vertical-align: top; text-align: left; }
164 table.attributes th { vertical-align: top; text-align: left; }
165 table.attributes td { vertical-align: top; }
165 table.attributes td { vertical-align: top; }
166
166
167 td.center {text-align:center;}
167 td.center {text-align:center;}
168
168
169 .highlight { background-color: #FCFD8D;}
169 .highlight { background-color: #FCFD8D;}
170 .highlight.token-1 { background-color: #faa;}
170 .highlight.token-1 { background-color: #faa;}
171 .highlight.token-2 { background-color: #afa;}
171 .highlight.token-2 { background-color: #afa;}
172 .highlight.token-3 { background-color: #aaf;}
172 .highlight.token-3 { background-color: #aaf;}
173
173
174 .box{
174 .box{
175 padding:6px;
175 padding:6px;
176 margin-bottom: 10px;
176 margin-bottom: 10px;
177 background-color:#f6f6f6;
177 background-color:#f6f6f6;
178 color:#505050;
178 color:#505050;
179 line-height:1.5em;
179 line-height:1.5em;
180 border: 1px solid #e4e4e4;
180 border: 1px solid #e4e4e4;
181 }
181 }
182
182
183 div.square {
183 div.square {
184 border: 1px solid #999;
184 border: 1px solid #999;
185 float: left;
185 float: left;
186 margin: .3em .4em 0 .4em;
186 margin: .3em .4em 0 .4em;
187 overflow: hidden;
187 overflow: hidden;
188 width: .6em; height: .6em;
188 width: .6em; height: .6em;
189 }
189 }
190 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
190 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
191 .contextual input, .contextual select {font-size:0.9em;}
191 .contextual input, .contextual select {font-size:0.9em;}
192 .message .contextual { margin-top: 0; }
192 .message .contextual { margin-top: 0; }
193
193
194 .splitcontentleft{float:left; width:49%;}
194 .splitcontentleft{float:left; width:49%;}
195 .splitcontentright{float:right; width:49%;}
195 .splitcontentright{float:right; width:49%;}
196 form {display: inline;}
196 form {display: inline;}
197 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
197 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
198 fieldset {border: 1px solid #e4e4e4; margin:0;}
198 fieldset {border: 1px solid #e4e4e4; margin:0;}
199 legend {color: #484848;}
199 legend {color: #484848;}
200 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
200 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
201 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
201 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
202 blockquote blockquote { margin-left: 0;}
202 blockquote blockquote { margin-left: 0;}
203 acronym { border-bottom: 1px dotted; cursor: help; }
203 acronym { border-bottom: 1px dotted; cursor: help; }
204 textarea.wiki-edit { width: 99%; }
204 textarea.wiki-edit { width: 99%; }
205 li p {margin-top: 0;}
205 li p {margin-top: 0;}
206 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
206 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
207 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
207 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
208 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
208 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
209 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
209 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
210
210
211 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
211 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
212 fieldset.collapsible legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
212 fieldset.collapsible legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
213 fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_collapsed.png); }
213 fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_collapsed.png); }
214
214
215 fieldset#date-range p { margin: 2px 0 2px 0; }
215 fieldset#date-range p { margin: 2px 0 2px 0; }
216 fieldset#filters table { border-collapse: collapse; }
216 fieldset#filters table { border-collapse: collapse; }
217 fieldset#filters table td { padding: 0; vertical-align: middle; }
217 fieldset#filters table td { padding: 0; vertical-align: middle; }
218 fieldset#filters tr.filter { height: 2em; }
218 fieldset#filters tr.filter { height: 2em; }
219 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
219 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
220 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
220 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
221
221
222 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
222 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
223 div#issue-changesets .changeset { padding: 4px;}
223 div#issue-changesets .changeset { padding: 4px;}
224 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
224 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
225 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
225 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
226
226
227 div#activity dl, #search-results { margin-left: 2em; }
227 div#activity dl, #search-results { margin-left: 2em; }
228 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
228 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
229 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
229 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
230 div#activity dt.me .time { border-bottom: 1px solid #999; }
230 div#activity dt.me .time { border-bottom: 1px solid #999; }
231 div#activity dt .time { color: #777; font-size: 80%; }
231 div#activity dt .time { color: #777; font-size: 80%; }
232 div#activity dd .description, #search-results dd .description { font-style: italic; }
232 div#activity dd .description, #search-results dd .description { font-style: italic; }
233 div#activity span.project:after, #search-results span.project:after { content: " -"; }
233 div#activity span.project:after, #search-results span.project:after { content: " -"; }
234 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
234 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
235
235
236 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
236 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
237
237
238 div#search-results-counts {float:right;}
238 div#search-results-counts {float:right;}
239 div#search-results-counts ul { margin-top: 0.5em; }
239 div#search-results-counts ul { margin-top: 0.5em; }
240 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
240 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
241
241
242 dt.issue { background-image: url(../images/ticket.png); }
242 dt.issue { background-image: url(../images/ticket.png); }
243 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
243 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
244 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
244 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
245 dt.issue-note { background-image: url(../images/ticket_note.png); }
245 dt.issue-note { background-image: url(../images/ticket_note.png); }
246 dt.changeset { background-image: url(../images/changeset.png); }
246 dt.changeset { background-image: url(../images/changeset.png); }
247 dt.news { background-image: url(../images/news.png); }
247 dt.news { background-image: url(../images/news.png); }
248 dt.message { background-image: url(../images/message.png); }
248 dt.message { background-image: url(../images/message.png); }
249 dt.reply { background-image: url(../images/comments.png); }
249 dt.reply { background-image: url(../images/comments.png); }
250 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
250 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
251 dt.attachment { background-image: url(../images/attachment.png); }
251 dt.attachment { background-image: url(../images/attachment.png); }
252 dt.document { background-image: url(../images/document.png); }
252 dt.document { background-image: url(../images/document.png); }
253 dt.project { background-image: url(../images/projects.png); }
253 dt.project { background-image: url(../images/projects.png); }
254 dt.time-entry { background-image: url(../images/time.png); }
254 dt.time-entry { background-image: url(../images/time.png); }
255
255
256 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
256 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
257
257
258 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
258 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
259 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
259 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
260 div#roadmap .wiki h1:first-child { display: none; }
260 div#roadmap .wiki h1:first-child { display: none; }
261 div#roadmap .wiki h1 { font-size: 120%; }
261 div#roadmap .wiki h1 { font-size: 120%; }
262 div#roadmap .wiki h2 { font-size: 110%; }
262 div#roadmap .wiki h2 { font-size: 110%; }
263
263
264 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
264 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
265 div#version-summary fieldset { margin-bottom: 1em; }
265 div#version-summary fieldset { margin-bottom: 1em; }
266 div#version-summary .total-hours { text-align: right; }
266 div#version-summary .total-hours { text-align: right; }
267
267
268 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
268 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
269 table#time-report tbody tr { font-style: italic; color: #777; }
269 table#time-report tbody tr { font-style: italic; color: #777; }
270 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
270 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
271 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
271 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
272 table#time-report .hours-dec { font-size: 0.9em; }
272 table#time-report .hours-dec { font-size: 0.9em; }
273
273
274 form#issue-form .attributes { margin-bottom: 8px; }
274 form#issue-form .attributes { margin-bottom: 8px; }
275 form#issue-form .attributes p { padding-top: 1px; padding-bottom: 2px; }
275 form#issue-form .attributes p { padding-top: 1px; padding-bottom: 2px; }
276 form#issue-form .attributes select { min-width: 30%; }
276 form#issue-form .attributes select { min-width: 30%; }
277
277
278 ul.projects { margin: 0; padding-left: 1em; }
278 ul.projects { margin: 0; padding-left: 1em; }
279 ul.projects.root { margin: 0; padding: 0; }
279 ul.projects.root { margin: 0; padding: 0; }
280 ul.projects ul { border-left: 3px solid #e0e0e0; }
280 ul.projects ul { border-left: 3px solid #e0e0e0; }
281 ul.projects li { list-style-type:none; }
281 ul.projects li { list-style-type:none; }
282 ul.projects li.root { margin-bottom: 1em; }
282 ul.projects li.root { margin-bottom: 1em; }
283 ul.projects li.child { margin-top: 1em;}
283 ul.projects li.child { margin-top: 1em;}
284 ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
284 ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
285 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
285 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
286
286
287 #tracker_project_ids ul { margin: 0; padding-left: 1em; }
287 #tracker_project_ids ul { margin: 0; padding-left: 1em; }
288 #tracker_project_ids li { list-style-type:none; }
288 #tracker_project_ids li { list-style-type:none; }
289
289
290 ul.properties {padding:0; font-size: 0.9em; color: #777;}
290 ul.properties {padding:0; font-size: 0.9em; color: #777;}
291 ul.properties li {list-style-type:none;}
291 ul.properties li {list-style-type:none;}
292 ul.properties li span {font-style:italic;}
292 ul.properties li span {font-style:italic;}
293
293
294 .total-hours { font-size: 110%; font-weight: bold; }
294 .total-hours { font-size: 110%; font-weight: bold; }
295 .total-hours span.hours-int { font-size: 120%; }
295 .total-hours span.hours-int { font-size: 120%; }
296
296
297 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
297 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
298 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
298 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
299
299
300 .pagination {font-size: 90%}
300 .pagination {font-size: 90%}
301 p.pagination {margin-top:8px;}
301 p.pagination {margin-top:8px;}
302
302
303 /***** Tabular forms ******/
303 /***** Tabular forms ******/
304 .tabular p{
304 .tabular p{
305 margin: 0;
305 margin: 0;
306 padding: 5px 0 8px 0;
306 padding: 5px 0 8px 0;
307 padding-left: 180px; /*width of left column containing the label elements*/
307 padding-left: 180px; /*width of left column containing the label elements*/
308 height: 1%;
308 height: 1%;
309 clear:left;
309 clear:left;
310 }
310 }
311
311
312 html>body .tabular p {overflow:hidden;}
312 html>body .tabular p {overflow:hidden;}
313
313
314 .tabular label{
314 .tabular label{
315 font-weight: bold;
315 font-weight: bold;
316 float: left;
316 float: left;
317 text-align: right;
317 text-align: right;
318 margin-left: -180px; /*width of left column*/
318 margin-left: -180px; /*width of left column*/
319 width: 175px; /*width of labels. Should be smaller than left column to create some right
319 width: 175px; /*width of labels. Should be smaller than left column to create some right
320 margin*/
320 margin*/
321 }
321 }
322
322
323 .tabular label.floating{
323 .tabular label.floating{
324 font-weight: normal;
324 font-weight: normal;
325 margin-left: 0px;
325 margin-left: 0px;
326 text-align: left;
326 text-align: left;
327 width: 270px;
327 width: 270px;
328 }
328 }
329
329
330 .tabular label.block{
330 .tabular label.block{
331 font-weight: normal;
331 font-weight: normal;
332 margin-left: 0px !important;
332 margin-left: 0px !important;
333 text-align: left;
333 text-align: left;
334 float: none;
334 float: none;
335 display: block;
335 display: block;
336 width: auto;
336 width: auto;
337 }
337 }
338
338
339 input#time_entry_comments { width: 90%;}
339 input#time_entry_comments { width: 90%;}
340
340
341 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
341 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
342
342
343 .tabular.settings p{ padding-left: 300px; }
343 .tabular.settings p{ padding-left: 300px; }
344 .tabular.settings label{ margin-left: -300px; width: 295px; }
344 .tabular.settings label{ margin-left: -300px; width: 295px; }
345
345
346 .required {color: #bb0000;}
346 .required {color: #bb0000;}
347 .summary {font-style: italic;}
347 .summary {font-style: italic;}
348
348
349 #attachments_fields input[type=text] {margin-left: 8px; }
349 #attachments_fields input[type=text] {margin-left: 8px; }
350
350
351 div.attachments { margin-top: 12px; }
351 div.attachments { margin-top: 12px; }
352 div.attachments p { margin:4px 0 2px 0; }
352 div.attachments p { margin:4px 0 2px 0; }
353 div.attachments img { vertical-align: middle; }
353 div.attachments img { vertical-align: middle; }
354 div.attachments span.author { font-size: 0.9em; color: #888; }
354 div.attachments span.author { font-size: 0.9em; color: #888; }
355
355
356 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
356 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
357 .other-formats span + span:before { content: "| "; }
357 .other-formats span + span:before { content: "| "; }
358
358
359 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
359 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
360
360
361 /* Project members tab */
361 /* Project members tab */
362 div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft, div#tab-content-users .splitcontentleft { width: 64% }
362 div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft, div#tab-content-users .splitcontentleft { width: 64% }
363 div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright, div#tab-content-users .splitcontentright { width: 34% }
363 div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright, div#tab-content-users .splitcontentright { width: 34% }
364 div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
364 div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
365 div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
365 div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
366 div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
366 div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
367 div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
367 div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
368
368
369 table.members td.group { padding-left: 20px; background: url(../images/users.png) no-repeat 0% 0%; }
369 table.members td.group { padding-left: 20px; background: url(../images/users.png) no-repeat 0% 0%; }
370
370
371 * html div#tab-content-members fieldset div { height: 450px; }
371 * html div#tab-content-members fieldset div { height: 450px; }
372
372
373 /***** Flash & error messages ****/
373 /***** Flash & error messages ****/
374 #errorExplanation, div.flash, .nodata, .warning {
374 #errorExplanation, div.flash, .nodata, .warning {
375 padding: 4px 4px 4px 30px;
375 padding: 4px 4px 4px 30px;
376 margin-bottom: 12px;
376 margin-bottom: 12px;
377 font-size: 1.1em;
377 font-size: 1.1em;
378 border: 2px solid;
378 border: 2px solid;
379 }
379 }
380
380
381 div.flash {margin-top: 8px;}
381 div.flash {margin-top: 8px;}
382
382
383 div.flash.error, #errorExplanation {
383 div.flash.error, #errorExplanation {
384 background: url(../images/false.png) 8px 5px no-repeat;
384 background: url(../images/false.png) 8px 5px no-repeat;
385 background-color: #ffe3e3;
385 background-color: #ffe3e3;
386 border-color: #dd0000;
386 border-color: #dd0000;
387 color: #550000;
387 color: #550000;
388 }
388 }
389
389
390 div.flash.notice {
390 div.flash.notice {
391 background: url(../images/true.png) 8px 5px no-repeat;
391 background: url(../images/true.png) 8px 5px no-repeat;
392 background-color: #dfffdf;
392 background-color: #dfffdf;
393 border-color: #9fcf9f;
393 border-color: #9fcf9f;
394 color: #005f00;
394 color: #005f00;
395 }
395 }
396
396
397 div.flash.warning {
397 div.flash.warning {
398 background: url(../images/warning.png) 8px 5px no-repeat;
398 background: url(../images/warning.png) 8px 5px no-repeat;
399 background-color: #FFEBC1;
399 background-color: #FFEBC1;
400 border-color: #FDBF3B;
400 border-color: #FDBF3B;
401 color: #A6750C;
401 color: #A6750C;
402 text-align: left;
402 text-align: left;
403 }
403 }
404
404
405 .nodata, .warning {
405 .nodata, .warning {
406 text-align: center;
406 text-align: center;
407 background-color: #FFEBC1;
407 background-color: #FFEBC1;
408 border-color: #FDBF3B;
408 border-color: #FDBF3B;
409 color: #A6750C;
409 color: #A6750C;
410 }
410 }
411
411
412 #errorExplanation ul { font-size: 0.9em;}
412 #errorExplanation ul { font-size: 0.9em;}
413 #errorExplanation h2, #errorExplanation p { display: none; }
413 #errorExplanation h2, #errorExplanation p { display: none; }
414
414
415 /***** Ajax indicator ******/
415 /***** Ajax indicator ******/
416 #ajax-indicator {
416 #ajax-indicator {
417 position: absolute; /* fixed not supported by IE */
417 position: absolute; /* fixed not supported by IE */
418 background-color:#eee;
418 background-color:#eee;
419 border: 1px solid #bbb;
419 border: 1px solid #bbb;
420 top:35%;
420 top:35%;
421 left:40%;
421 left:40%;
422 width:20%;
422 width:20%;
423 font-weight:bold;
423 font-weight:bold;
424 text-align:center;
424 text-align:center;
425 padding:0.6em;
425 padding:0.6em;
426 z-index:100;
426 z-index:100;
427 filter:alpha(opacity=50);
427 filter:alpha(opacity=50);
428 opacity: 0.5;
428 opacity: 0.5;
429 }
429 }
430
430
431 html>body #ajax-indicator { position: fixed; }
431 html>body #ajax-indicator { position: fixed; }
432
432
433 #ajax-indicator span {
433 #ajax-indicator span {
434 background-position: 0% 40%;
434 background-position: 0% 40%;
435 background-repeat: no-repeat;
435 background-repeat: no-repeat;
436 background-image: url(../images/loading.gif);
436 background-image: url(../images/loading.gif);
437 padding-left: 26px;
437 padding-left: 26px;
438 vertical-align: bottom;
438 vertical-align: bottom;
439 }
439 }
440
440
441 /***** Calendar *****/
441 /***** Calendar *****/
442 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
442 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
443 table.cal thead th {width: 14%;}
443 table.cal thead th {width: 14%;}
444 table.cal tbody tr {height: 100px;}
444 table.cal tbody tr {height: 100px;}
445 table.cal th { background-color:#EEEEEE; padding: 4px; }
445 table.cal th { background-color:#EEEEEE; padding: 4px; }
446 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
446 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
447 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
447 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
448 table.cal td.odd p.day-num {color: #bbb;}
448 table.cal td.odd p.day-num {color: #bbb;}
449 table.cal td.today {background:#ffffdd;}
449 table.cal td.today {background:#ffffdd;}
450 table.cal td.today p.day-num {font-weight: bold;}
450 table.cal td.today p.day-num {font-weight: bold;}
451
451
452 /***** Tooltips ******/
452 /***** Tooltips ******/
453 .tooltip{position:relative;z-index:24;}
453 .tooltip{position:relative;z-index:24;}
454 .tooltip:hover{z-index:25;color:#000;}
454 .tooltip:hover{z-index:25;color:#000;}
455 .tooltip span.tip{display: none; text-align:left;}
455 .tooltip span.tip{display: none; text-align:left;}
456
456
457 div.tooltip:hover span.tip{
457 div.tooltip:hover span.tip{
458 display:block;
458 display:block;
459 position:absolute;
459 position:absolute;
460 top:12px; left:24px; width:270px;
460 top:12px; left:24px; width:270px;
461 border:1px solid #555;
461 border:1px solid #555;
462 background-color:#fff;
462 background-color:#fff;
463 padding: 4px;
463 padding: 4px;
464 font-size: 0.8em;
464 font-size: 0.8em;
465 color:#505050;
465 color:#505050;
466 }
466 }
467
467
468 /***** Progress bar *****/
468 /***** Progress bar *****/
469 table.progress {
469 table.progress {
470 border: 1px solid #D7D7D7;
470 border: 1px solid #D7D7D7;
471 border-collapse: collapse;
471 border-collapse: collapse;
472 border-spacing: 0pt;
472 border-spacing: 0pt;
473 empty-cells: show;
473 empty-cells: show;
474 text-align: center;
474 text-align: center;
475 float:left;
475 float:left;
476 margin: 1px 6px 1px 0px;
476 margin: 1px 6px 1px 0px;
477 }
477 }
478
478
479 table.progress td { height: 0.9em; }
479 table.progress td { height: 0.9em; }
480 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
480 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
481 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
481 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
482 table.progress td.open { background: #FFF none repeat scroll 0%; }
482 table.progress td.open { background: #FFF none repeat scroll 0%; }
483 p.pourcent {font-size: 80%;}
483 p.pourcent {font-size: 80%;}
484 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
484 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
485
485
486 /***** Tabs *****/
486 /***** Tabs *****/
487 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
487 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
488 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
488 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
489 #content .tabs>ul { bottom:-1px; } /* others */
489 #content .tabs>ul { bottom:-1px; } /* others */
490 #content .tabs ul li {
490 #content .tabs ul li {
491 float:left;
491 float:left;
492 list-style-type:none;
492 list-style-type:none;
493 white-space:nowrap;
493 white-space:nowrap;
494 margin-right:8px;
494 margin-right:8px;
495 background:#fff;
495 background:#fff;
496 }
496 }
497 #content .tabs ul li a{
497 #content .tabs ul li a{
498 display:block;
498 display:block;
499 font-size: 0.9em;
499 font-size: 0.9em;
500 text-decoration:none;
500 text-decoration:none;
501 line-height:1.3em;
501 line-height:1.3em;
502 padding:4px 6px 4px 6px;
502 padding:4px 6px 4px 6px;
503 border: 1px solid #ccc;
503 border: 1px solid #ccc;
504 border-bottom: 1px solid #bbbbbb;
504 border-bottom: 1px solid #bbbbbb;
505 background-color: #eeeeee;
505 background-color: #eeeeee;
506 color:#777;
506 color:#777;
507 font-weight:bold;
507 font-weight:bold;
508 }
508 }
509
509
510 #content .tabs ul li a:hover {
510 #content .tabs ul li a:hover {
511 background-color: #ffffdd;
511 background-color: #ffffdd;
512 text-decoration:none;
512 text-decoration:none;
513 }
513 }
514
514
515 #content .tabs ul li a.selected {
515 #content .tabs ul li a.selected {
516 background-color: #fff;
516 background-color: #fff;
517 border: 1px solid #bbbbbb;
517 border: 1px solid #bbbbbb;
518 border-bottom: 1px solid #fff;
518 border-bottom: 1px solid #fff;
519 }
519 }
520
520
521 #content .tabs ul li a.selected:hover {
521 #content .tabs ul li a.selected:hover {
522 background-color: #fff;
522 background-color: #fff;
523 }
523 }
524
524
525 /***** Auto-complete *****/
525 /***** Auto-complete *****/
526 div.autocomplete {
526 div.autocomplete {
527 position:absolute;
527 position:absolute;
528 width:250px;
528 width:250px;
529 background-color:white;
529 background-color:white;
530 margin:0;
530 margin:0;
531 padding:0;
531 padding:0;
532 }
532 }
533 div.autocomplete ul {
533 div.autocomplete ul {
534 list-style-type:none;
534 list-style-type:none;
535 margin:0;
535 margin:0;
536 padding:0;
536 padding:0;
537 }
537 }
538 div.autocomplete ul li.selected { background-color: #ffb;}
538 div.autocomplete ul li.selected { background-color: #ffb;}
539 div.autocomplete ul li {
539 div.autocomplete ul li {
540 list-style-type:none;
540 list-style-type:none;
541 display:block;
541 display:block;
542 margin:0;
542 margin:0;
543 padding:2px;
543 padding:2px;
544 cursor:pointer;
544 cursor:pointer;
545 font-size: 90%;
545 font-size: 90%;
546 border-bottom: 1px solid #ccc;
546 border-bottom: 1px solid #ccc;
547 border-left: 1px solid #ccc;
547 border-left: 1px solid #ccc;
548 border-right: 1px solid #ccc;
548 border-right: 1px solid #ccc;
549 }
549 }
550 div.autocomplete ul li span.informal {
550 div.autocomplete ul li span.informal {
551 font-size: 80%;
551 font-size: 80%;
552 color: #aaa;
552 color: #aaa;
553 }
553 }
554
554
555 /***** Diff *****/
555 /***** Diff *****/
556 .diff_out { background: #fcc; }
556 .diff_out { background: #fcc; }
557 .diff_in { background: #cfc; }
557 .diff_in { background: #cfc; }
558
558
559 /***** Wiki *****/
559 /***** Wiki *****/
560 div.wiki table {
560 div.wiki table {
561 border: 1px solid #505050;
561 border: 1px solid #505050;
562 border-collapse: collapse;
562 border-collapse: collapse;
563 margin-bottom: 1em;
563 margin-bottom: 1em;
564 }
564 }
565
565
566 div.wiki table, div.wiki td, div.wiki th {
566 div.wiki table, div.wiki td, div.wiki th {
567 border: 1px solid #bbb;
567 border: 1px solid #bbb;
568 padding: 4px;
568 padding: 4px;
569 }
569 }
570
570
571 div.wiki .external {
571 div.wiki .external {
572 background-position: 0% 60%;
572 background-position: 0% 60%;
573 background-repeat: no-repeat;
573 background-repeat: no-repeat;
574 padding-left: 12px;
574 padding-left: 12px;
575 background-image: url(../images/external.png);
575 background-image: url(../images/external.png);
576 }
576 }
577
577
578 div.wiki a.new {
578 div.wiki a.new {
579 color: #b73535;
579 color: #b73535;
580 }
580 }
581
581
582 div.wiki pre {
582 div.wiki pre {
583 margin: 1em 1em 1em 1.6em;
583 margin: 1em 1em 1em 1.6em;
584 padding: 2px;
584 padding: 2px;
585 background-color: #fafafa;
585 background-color: #fafafa;
586 border: 1px solid #dadada;
586 border: 1px solid #dadada;
587 width:95%;
587 width:95%;
588 overflow-x: auto;
588 overflow-x: auto;
589 }
589 }
590
590
591 div.wiki ul.toc {
591 div.wiki ul.toc {
592 background-color: #ffffdd;
592 background-color: #ffffdd;
593 border: 1px solid #e4e4e4;
593 border: 1px solid #e4e4e4;
594 padding: 4px;
594 padding: 4px;
595 line-height: 1.2em;
595 line-height: 1.2em;
596 margin-bottom: 12px;
596 margin-bottom: 12px;
597 margin-right: 12px;
597 margin-right: 12px;
598 margin-left: 0;
598 margin-left: 0;
599 display: table
599 display: table
600 }
600 }
601 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
601 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
602
602
603 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
603 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
604 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
604 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
605 div.wiki ul.toc li { list-style-type:none;}
605 div.wiki ul.toc li { list-style-type:none;}
606 div.wiki ul.toc li.heading2 { margin-left: 6px; }
606 div.wiki ul.toc li.heading2 { margin-left: 6px; }
607 div.wiki ul.toc li.heading3 { margin-left: 12px; font-size: 0.8em; }
607 div.wiki ul.toc li.heading3 { margin-left: 12px; font-size: 0.8em; }
608
608
609 div.wiki ul.toc a {
609 div.wiki ul.toc a {
610 font-size: 0.9em;
610 font-size: 0.9em;
611 font-weight: normal;
611 font-weight: normal;
612 text-decoration: none;
612 text-decoration: none;
613 color: #606060;
613 color: #606060;
614 }
614 }
615 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
615 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
616
616
617 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
617 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
618 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
618 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
619 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
619 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
620
620
621 /***** My page layout *****/
621 /***** My page layout *****/
622 .block-receiver {
622 .block-receiver {
623 border:1px dashed #c0c0c0;
623 border:1px dashed #c0c0c0;
624 margin-bottom: 20px;
624 margin-bottom: 20px;
625 padding: 15px 0 15px 0;
625 padding: 15px 0 15px 0;
626 }
626 }
627
627
628 .mypage-box {
628 .mypage-box {
629 margin:0 0 20px 0;
629 margin:0 0 20px 0;
630 color:#505050;
630 color:#505050;
631 line-height:1.5em;
631 line-height:1.5em;
632 }
632 }
633
633
634 .handle {
634 .handle {
635 cursor: move;
635 cursor: move;
636 }
636 }
637
637
638 a.close-icon {
638 a.close-icon {
639 display:block;
639 display:block;
640 margin-top:3px;
640 margin-top:3px;
641 overflow:hidden;
641 overflow:hidden;
642 width:12px;
642 width:12px;
643 height:12px;
643 height:12px;
644 background-repeat: no-repeat;
644 background-repeat: no-repeat;
645 cursor:pointer;
645 cursor:pointer;
646 background-image:url('../images/close.png');
646 background-image:url('../images/close.png');
647 }
647 }
648
648
649 a.close-icon:hover {
649 a.close-icon:hover {
650 background-image:url('../images/close_hl.png');
650 background-image:url('../images/close_hl.png');
651 }
651 }
652
652
653 /***** Gantt chart *****/
653 /***** Gantt chart *****/
654 .gantt_hdr {
654 .gantt_hdr {
655 position:absolute;
655 position:absolute;
656 top:0;
656 top:0;
657 height:16px;
657 height:16px;
658 border-top: 1px solid #c0c0c0;
658 border-top: 1px solid #c0c0c0;
659 border-bottom: 1px solid #c0c0c0;
659 border-bottom: 1px solid #c0c0c0;
660 border-right: 1px solid #c0c0c0;
660 border-right: 1px solid #c0c0c0;
661 text-align: center;
661 text-align: center;
662 overflow: hidden;
662 overflow: hidden;
663 }
663 }
664
664
665 .task {
665 .task {
666 position: absolute;
666 position: absolute;
667 height:8px;
667 height:8px;
668 font-size:0.8em;
668 font-size:0.8em;
669 color:#888;
669 color:#888;
670 padding:0;
670 padding:0;
671 margin:0;
671 margin:0;
672 line-height:0.8em;
672 line-height:0.8em;
673 }
673 }
674
674
675 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
675 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
676 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
676 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
677 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
677 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
678 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
678 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
679
679
680 /***** Icons *****/
680 /***** Icons *****/
681 .icon {
681 .icon {
682 background-position: 0% 40%;
682 background-position: 0% 40%;
683 background-repeat: no-repeat;
683 background-repeat: no-repeat;
684 padding-left: 20px;
684 padding-left: 20px;
685 padding-top: 2px;
685 padding-top: 2px;
686 padding-bottom: 3px;
686 padding-bottom: 3px;
687 }
687 }
688
688
689 .icon22 {
689 .icon22 {
690 background-position: 0% 40%;
690 background-position: 0% 40%;
691 background-repeat: no-repeat;
691 background-repeat: no-repeat;
692 padding-left: 26px;
692 padding-left: 26px;
693 line-height: 22px;
693 line-height: 22px;
694 vertical-align: middle;
694 vertical-align: middle;
695 }
695 }
696
696
697 .icon-add { background-image: url(../images/add.png); }
697 .icon-add { background-image: url(../images/add.png); }
698 .icon-edit { background-image: url(../images/edit.png); }
698 .icon-edit { background-image: url(../images/edit.png); }
699 .icon-copy { background-image: url(../images/copy.png); }
699 .icon-copy { background-image: url(../images/copy.png); }
700 .icon-duplicate { background-image: url(../images/duplicate.png); }
700 .icon-duplicate { background-image: url(../images/duplicate.png); }
701 .icon-del { background-image: url(../images/delete.png); }
701 .icon-del { background-image: url(../images/delete.png); }
702 .icon-move { background-image: url(../images/move.png); }
702 .icon-move { background-image: url(../images/move.png); }
703 .icon-save { background-image: url(../images/save.png); }
703 .icon-save { background-image: url(../images/save.png); }
704 .icon-cancel { background-image: url(../images/cancel.png); }
704 .icon-cancel { background-image: url(../images/cancel.png); }
705 .icon-multiple { background-image: url(../images/table_multiple.png); }
705 .icon-folder { background-image: url(../images/folder.png); }
706 .icon-folder { background-image: url(../images/folder.png); }
706 .open .icon-folder { background-image: url(../images/folder_open.png); }
707 .open .icon-folder { background-image: url(../images/folder_open.png); }
707 .icon-package { background-image: url(../images/package.png); }
708 .icon-package { background-image: url(../images/package.png); }
708 .icon-home { background-image: url(../images/home.png); }
709 .icon-home { background-image: url(../images/home.png); }
709 .icon-user { background-image: url(../images/user.png); }
710 .icon-user { background-image: url(../images/user.png); }
710 .icon-mypage { background-image: url(../images/user_page.png); }
711 .icon-mypage { background-image: url(../images/user_page.png); }
711 .icon-admin { background-image: url(../images/admin.png); }
712 .icon-admin { background-image: url(../images/admin.png); }
712 .icon-projects { background-image: url(../images/projects.png); }
713 .icon-projects { background-image: url(../images/projects.png); }
713 .icon-help { background-image: url(../images/help.png); }
714 .icon-help { background-image: url(../images/help.png); }
714 .icon-attachment { background-image: url(../images/attachment.png); }
715 .icon-attachment { background-image: url(../images/attachment.png); }
715 .icon-index { background-image: url(../images/index.png); }
716 .icon-index { background-image: url(../images/index.png); }
716 .icon-history { background-image: url(../images/history.png); }
717 .icon-history { background-image: url(../images/history.png); }
717 .icon-time { background-image: url(../images/time.png); }
718 .icon-time { background-image: url(../images/time.png); }
718 .icon-time-add { background-image: url(../images/time_add.png); }
719 .icon-time-add { background-image: url(../images/time_add.png); }
719 .icon-stats { background-image: url(../images/stats.png); }
720 .icon-stats { background-image: url(../images/stats.png); }
720 .icon-warning { background-image: url(../images/warning.png); }
721 .icon-warning { background-image: url(../images/warning.png); }
721 .icon-fav { background-image: url(../images/fav.png); }
722 .icon-fav { background-image: url(../images/fav.png); }
722 .icon-fav-off { background-image: url(../images/fav_off.png); }
723 .icon-fav-off { background-image: url(../images/fav_off.png); }
723 .icon-reload { background-image: url(../images/reload.png); }
724 .icon-reload { background-image: url(../images/reload.png); }
724 .icon-lock { background-image: url(../images/locked.png); }
725 .icon-lock { background-image: url(../images/locked.png); }
725 .icon-unlock { background-image: url(../images/unlock.png); }
726 .icon-unlock { background-image: url(../images/unlock.png); }
726 .icon-checked { background-image: url(../images/true.png); }
727 .icon-checked { background-image: url(../images/true.png); }
727 .icon-details { background-image: url(../images/zoom_in.png); }
728 .icon-details { background-image: url(../images/zoom_in.png); }
728 .icon-report { background-image: url(../images/report.png); }
729 .icon-report { background-image: url(../images/report.png); }
729 .icon-comment { background-image: url(../images/comment.png); }
730 .icon-comment { background-image: url(../images/comment.png); }
730
731
731 .icon-file { background-image: url(../images/files/default.png); }
732 .icon-file { background-image: url(../images/files/default.png); }
732 .icon-file.text-plain { background-image: url(../images/files/text.png); }
733 .icon-file.text-plain { background-image: url(../images/files/text.png); }
733 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
734 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
734 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
735 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
735 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
736 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
736 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
737 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
737 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
738 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
738 .icon-file.image-gif { background-image: url(../images/files/image.png); }
739 .icon-file.image-gif { background-image: url(../images/files/image.png); }
739 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
740 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
740 .icon-file.image-png { background-image: url(../images/files/image.png); }
741 .icon-file.image-png { background-image: url(../images/files/image.png); }
741 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
742 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
742 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
743 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
743 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
744 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
744 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
745 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
745
746
746 .icon22-projects { background-image: url(../images/22x22/projects.png); }
747 .icon22-projects { background-image: url(../images/22x22/projects.png); }
747 .icon22-users { background-image: url(../images/22x22/users.png); }
748 .icon22-users { background-image: url(../images/22x22/users.png); }
748 .icon22-groups { background-image: url(../images/22x22/groups.png); }
749 .icon22-groups { background-image: url(../images/22x22/groups.png); }
749 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
750 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
750 .icon22-role { background-image: url(../images/22x22/role.png); }
751 .icon22-role { background-image: url(../images/22x22/role.png); }
751 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
752 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
752 .icon22-options { background-image: url(../images/22x22/options.png); }
753 .icon22-options { background-image: url(../images/22x22/options.png); }
753 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
754 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
754 .icon22-authent { background-image: url(../images/22x22/authent.png); }
755 .icon22-authent { background-image: url(../images/22x22/authent.png); }
755 .icon22-info { background-image: url(../images/22x22/info.png); }
756 .icon22-info { background-image: url(../images/22x22/info.png); }
756 .icon22-comment { background-image: url(../images/22x22/comment.png); }
757 .icon22-comment { background-image: url(../images/22x22/comment.png); }
757 .icon22-package { background-image: url(../images/22x22/package.png); }
758 .icon22-package { background-image: url(../images/22x22/package.png); }
758 .icon22-settings { background-image: url(../images/22x22/settings.png); }
759 .icon22-settings { background-image: url(../images/22x22/settings.png); }
759 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
760 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
760
761
761 img.gravatar {
762 img.gravatar {
762 padding: 2px;
763 padding: 2px;
763 border: solid 1px #d5d5d5;
764 border: solid 1px #d5d5d5;
764 background: #fff;
765 background: #fff;
765 }
766 }
766
767
767 div.issue img.gravatar {
768 div.issue img.gravatar {
768 float: right;
769 float: right;
769 margin: 0 0 0 1em;
770 margin: 0 0 0 1em;
770 padding: 5px;
771 padding: 5px;
771 }
772 }
772
773
773 div.issue table img.gravatar {
774 div.issue table img.gravatar {
774 height: 14px;
775 height: 14px;
775 width: 14px;
776 width: 14px;
776 padding: 2px;
777 padding: 2px;
777 float: left;
778 float: left;
778 margin: 0 0.5em 0 0;
779 margin: 0 0.5em 0 0;
779 }
780 }
780
781
781 #history img.gravatar {
782 #history img.gravatar {
782 padding: 3px;
783 padding: 3px;
783 margin: 0 1.5em 1em 0;
784 margin: 0 1.5em 1em 0;
784 float: left;
785 float: left;
785 }
786 }
786
787
787 td.username img.gravatar {
788 td.username img.gravatar {
788 float: left;
789 float: left;
789 margin: 0 1em 0 0;
790 margin: 0 1em 0 0;
790 }
791 }
791
792
792 #activity dt img.gravatar {
793 #activity dt img.gravatar {
793 float: left;
794 float: left;
794 margin: 0 1em 1em 0;
795 margin: 0 1em 1em 0;
795 }
796 }
796
797
797 #activity dt,
798 #activity dt,
798 .journal {
799 .journal {
799 clear: left;
800 clear: left;
800 }
801 }
801
802
802 .gravatar-margin {
803 .gravatar-margin {
803 margin-left: 40px;
804 margin-left: 40px;
804 }
805 }
805
806
806 h2 img { vertical-align:middle; }
807 h2 img { vertical-align:middle; }
807
808
808
809
809 /***** Media print specific styles *****/
810 /***** Media print specific styles *****/
810 @media print {
811 @media print {
811 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
812 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
812 #main { background: #fff; }
813 #main { background: #fff; }
813 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
814 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
814 #wiki_add_attachment { display:none; }
815 #wiki_add_attachment { display:none; }
815 }
816 }
@@ -1,73 +1,96
1 require File.dirname(__FILE__) + '/../test_helper'
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'issue_statuses_controller'
2 require 'issue_statuses_controller'
3
3
4 # Re-raise errors caught by the controller.
4 # Re-raise errors caught by the controller.
5 class IssueStatusesController; def rescue_action(e) raise e end; end
5 class IssueStatusesController; def rescue_action(e) raise e end; end
6
6
7
7
8 class IssueStatusesControllerTest < ActionController::TestCase
8 class IssueStatusesControllerTest < ActionController::TestCase
9 fixtures :issue_statuses, :issues
9 fixtures :issue_statuses, :issues
10
10
11 def setup
11 def setup
12 @controller = IssueStatusesController.new
12 @controller = IssueStatusesController.new
13 @request = ActionController::TestRequest.new
13 @request = ActionController::TestRequest.new
14 @response = ActionController::TestResponse.new
14 @response = ActionController::TestResponse.new
15 User.current = nil
15 User.current = nil
16 @request.session[:user_id] = 1 # admin
16 @request.session[:user_id] = 1 # admin
17 end
17 end
18
18
19 def test_index
19 def test_index
20 # TODO: unify with #list
20 # TODO: unify with #list
21 get :index
21 get :index
22 assert_response :success
22 assert_response :success
23 assert_template 'list'
23 assert_template 'list'
24 end
24 end
25
25
26 def test_new
26 def test_new
27 get :new
27 get :new
28 assert_response :success
28 assert_response :success
29 assert_template 'new'
29 assert_template 'new'
30 end
30 end
31
31
32 def test_create
32 def test_create
33 assert_difference 'IssueStatus.count' do
33 assert_difference 'IssueStatus.count' do
34 post :create, :issue_status => {:name => 'New status'}
34 post :create, :issue_status => {:name => 'New status'}
35 end
35 end
36 assert_redirected_to 'issue_statuses/list'
36 assert_redirected_to 'issue_statuses/list'
37 status = IssueStatus.find(:first, :order => 'id DESC')
37 status = IssueStatus.find(:first, :order => 'id DESC')
38 assert_equal 'New status', status.name
38 assert_equal 'New status', status.name
39 end
39 end
40
40
41 def test_edit
41 def test_edit
42 get :edit, :id => '3'
42 get :edit, :id => '3'
43 assert_response :success
43 assert_response :success
44 assert_template 'edit'
44 assert_template 'edit'
45 end
45 end
46
46
47 def test_update
47 def test_update
48 post :update, :id => '3', :issue_status => {:name => 'Renamed status'}
48 post :update, :id => '3', :issue_status => {:name => 'Renamed status'}
49 assert_redirected_to 'issue_statuses/list'
49 assert_redirected_to 'issue_statuses/list'
50 status = IssueStatus.find(3)
50 status = IssueStatus.find(3)
51 assert_equal 'Renamed status', status.name
51 assert_equal 'Renamed status', status.name
52 end
52 end
53
53
54 def test_destroy
54 def test_destroy
55 Issue.delete_all("status_id = 1")
55 Issue.delete_all("status_id = 1")
56
56
57 assert_difference 'IssueStatus.count', -1 do
57 assert_difference 'IssueStatus.count', -1 do
58 post :destroy, :id => '1'
58 post :destroy, :id => '1'
59 end
59 end
60 assert_redirected_to 'issue_statuses/list'
60 assert_redirected_to 'issue_statuses/list'
61 assert_nil IssueStatus.find_by_id(1)
61 assert_nil IssueStatus.find_by_id(1)
62 end
62 end
63
63
64 def test_destroy_should_block_if_status_in_use
64 def test_destroy_should_block_if_status_in_use
65 assert_not_nil Issue.find_by_status_id(1)
65 assert_not_nil Issue.find_by_status_id(1)
66
66
67 assert_no_difference 'IssueStatus.count' do
67 assert_no_difference 'IssueStatus.count' do
68 post :destroy, :id => '1'
68 post :destroy, :id => '1'
69 end
69 end
70 assert_redirected_to 'issue_statuses/list'
70 assert_redirected_to 'issue_statuses/list'
71 assert_not_nil IssueStatus.find_by_id(1)
71 assert_not_nil IssueStatus.find_by_id(1)
72 end
72 end
73
74 context "on POST to :update_issue_done_ratio" do
75 context "with Setting.issue_done_ratio using the issue_field" do
76 setup do
77 Setting.issue_done_ratio = 'issue_field'
78 post :update_issue_done_ratio
79 end
80
81 should_set_the_flash_to /not updated/
82 should_redirect_to('the list') { '/issue_statuses/list' }
83 end
84
85 context "with Setting.issue_done_ratio using the issue_status" do
86 setup do
87 Setting.issue_done_ratio = 'issue_status'
88 post :update_issue_done_ratio
89 end
90
91 should_set_the_flash_to /Issue done ratios updated/
92 should_redirect_to('the list') { '/issue_statuses/list' }
93 end
94 end
95
73 end
96 end
@@ -1,69 +1,105
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class IssueStatusTest < ActiveSupport::TestCase
20 class IssueStatusTest < ActiveSupport::TestCase
21 fixtures :issue_statuses, :issues
21 fixtures :issue_statuses, :issues
22
22
23 def test_create
23 def test_create
24 status = IssueStatus.new :name => "Assigned"
24 status = IssueStatus.new :name => "Assigned"
25 assert !status.save
25 assert !status.save
26 # status name uniqueness
26 # status name uniqueness
27 assert_equal 1, status.errors.count
27 assert_equal 1, status.errors.count
28
28
29 status.name = "Test Status"
29 status.name = "Test Status"
30 assert status.save
30 assert status.save
31 assert !status.is_default
31 assert !status.is_default
32 end
32 end
33
33
34 def test_destroy
34 def test_destroy
35 count_before = IssueStatus.count
35 count_before = IssueStatus.count
36 status = IssueStatus.find(3)
36 status = IssueStatus.find(3)
37 assert status.destroy
37 assert status.destroy
38 assert_equal count_before - 1, IssueStatus.count
38 assert_equal count_before - 1, IssueStatus.count
39 end
39 end
40
40
41 def test_destroy_status_in_use
41 def test_destroy_status_in_use
42 # Status assigned to an Issue
42 # Status assigned to an Issue
43 status = Issue.find(1).status
43 status = Issue.find(1).status
44 assert_raise(RuntimeError, "Can't delete status") { status.destroy }
44 assert_raise(RuntimeError, "Can't delete status") { status.destroy }
45 end
45 end
46
46
47 def test_default
47 def test_default
48 status = IssueStatus.default
48 status = IssueStatus.default
49 assert_kind_of IssueStatus, status
49 assert_kind_of IssueStatus, status
50 end
50 end
51
51
52 def test_change_default
52 def test_change_default
53 status = IssueStatus.find(2)
53 status = IssueStatus.find(2)
54 assert !status.is_default
54 assert !status.is_default
55 status.is_default = true
55 status.is_default = true
56 assert status.save
56 assert status.save
57 status.reload
57 status.reload
58
58
59 assert_equal status, IssueStatus.default
59 assert_equal status, IssueStatus.default
60 assert !IssueStatus.find(1).is_default
60 assert !IssueStatus.find(1).is_default
61 end
61 end
62
62
63 def test_reorder_should_not_clear_default_status
63 def test_reorder_should_not_clear_default_status
64 status = IssueStatus.default
64 status = IssueStatus.default
65 status.move_to_bottom
65 status.move_to_bottom
66 status.reload
66 status.reload
67 assert status.is_default?
67 assert status.is_default?
68 end
68 end
69
70 context "#update_done_ratios" do
71 setup do
72 @issue = Issue.find(1)
73 @issue_status = IssueStatus.find(1)
74 @issue_status.update_attribute(:default_done_ratio, 50)
75 end
76
77 context "with Setting.issue_done_ratio using the issue_field" do
78 setup do
79 Setting.issue_done_ratio = 'issue_field'
80 end
81
82 should "change nothing" do
83 IssueStatus.update_issue_done_ratios
84
85 assert_equal 0, Issue.count(:conditions => {:done_ratio => 50})
86 end
87 end
88
89 context "with Setting.issue_done_ratio using the issue_status" do
90 setup do
91 Setting.issue_done_ratio = 'issue_status'
92 end
93
94 should "update all of the issue's done_ratios to match their Issue Status" do
95 IssueStatus.update_issue_done_ratios
96
97 issues = Issue.find([1,3,4,5,6,7,9,10])
98 issues.each do |issue|
99 assert_equal @issue_status, issue.status
100 assert_equal 50, issue.read_attribute(:done_ratio)
101 end
102 end
103 end
104 end
69 end
105 end
@@ -1,532 +1,592
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :members, :member_roles, :roles,
21 fixtures :projects, :users, :members, :member_roles, :roles,
22 :trackers, :projects_trackers,
22 :trackers, :projects_trackers,
23 :versions,
23 :versions,
24 :issue_statuses, :issue_categories, :issue_relations, :workflows,
24 :issue_statuses, :issue_categories, :issue_relations, :workflows,
25 :enumerations,
25 :enumerations,
26 :issues,
26 :issues,
27 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
27 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
28 :time_entries
28 :time_entries
29
29
30 def test_create
30 def test_create
31 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :description => 'IssueTest#test_create', :estimated_hours => '1:30')
31 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :description => 'IssueTest#test_create', :estimated_hours => '1:30')
32 assert issue.save
32 assert issue.save
33 issue.reload
33 issue.reload
34 assert_equal 1.5, issue.estimated_hours
34 assert_equal 1.5, issue.estimated_hours
35 end
35 end
36
36
37 def test_create_minimal
37 def test_create_minimal
38 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create')
38 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create')
39 assert issue.save
39 assert issue.save
40 assert issue.description.nil?
40 assert issue.description.nil?
41 end
41 end
42
42
43 def test_create_with_required_custom_field
43 def test_create_with_required_custom_field
44 field = IssueCustomField.find_by_name('Database')
44 field = IssueCustomField.find_by_name('Database')
45 field.update_attribute(:is_required, true)
45 field.update_attribute(:is_required, true)
46
46
47 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'test_create', :description => 'IssueTest#test_create_with_required_custom_field')
47 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'test_create', :description => 'IssueTest#test_create_with_required_custom_field')
48 assert issue.available_custom_fields.include?(field)
48 assert issue.available_custom_fields.include?(field)
49 # No value for the custom field
49 # No value for the custom field
50 assert !issue.save
50 assert !issue.save
51 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
51 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
52 # Blank value
52 # Blank value
53 issue.custom_field_values = { field.id => '' }
53 issue.custom_field_values = { field.id => '' }
54 assert !issue.save
54 assert !issue.save
55 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
55 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
56 # Invalid value
56 # Invalid value
57 issue.custom_field_values = { field.id => 'SQLServer' }
57 issue.custom_field_values = { field.id => 'SQLServer' }
58 assert !issue.save
58 assert !issue.save
59 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
59 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
60 # Valid value
60 # Valid value
61 issue.custom_field_values = { field.id => 'PostgreSQL' }
61 issue.custom_field_values = { field.id => 'PostgreSQL' }
62 assert issue.save
62 assert issue.save
63 issue.reload
63 issue.reload
64 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
64 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
65 end
65 end
66
66
67 def test_visible_scope_for_anonymous
67 def test_visible_scope_for_anonymous
68 # Anonymous user should see issues of public projects only
68 # Anonymous user should see issues of public projects only
69 issues = Issue.visible(User.anonymous).all
69 issues = Issue.visible(User.anonymous).all
70 assert issues.any?
70 assert issues.any?
71 assert_nil issues.detect {|issue| !issue.project.is_public?}
71 assert_nil issues.detect {|issue| !issue.project.is_public?}
72 # Anonymous user should not see issues without permission
72 # Anonymous user should not see issues without permission
73 Role.anonymous.remove_permission!(:view_issues)
73 Role.anonymous.remove_permission!(:view_issues)
74 issues = Issue.visible(User.anonymous).all
74 issues = Issue.visible(User.anonymous).all
75 assert issues.empty?
75 assert issues.empty?
76 end
76 end
77
77
78 def test_visible_scope_for_user
78 def test_visible_scope_for_user
79 user = User.find(9)
79 user = User.find(9)
80 assert user.projects.empty?
80 assert user.projects.empty?
81 # Non member user should see issues of public projects only
81 # Non member user should see issues of public projects only
82 issues = Issue.visible(user).all
82 issues = Issue.visible(user).all
83 assert issues.any?
83 assert issues.any?
84 assert_nil issues.detect {|issue| !issue.project.is_public?}
84 assert_nil issues.detect {|issue| !issue.project.is_public?}
85 # Non member user should not see issues without permission
85 # Non member user should not see issues without permission
86 Role.non_member.remove_permission!(:view_issues)
86 Role.non_member.remove_permission!(:view_issues)
87 user.reload
87 user.reload
88 issues = Issue.visible(user).all
88 issues = Issue.visible(user).all
89 assert issues.empty?
89 assert issues.empty?
90 # User should see issues of projects for which he has view_issues permissions only
90 # User should see issues of projects for which he has view_issues permissions only
91 Member.create!(:principal => user, :project_id => 2, :role_ids => [1])
91 Member.create!(:principal => user, :project_id => 2, :role_ids => [1])
92 user.reload
92 user.reload
93 issues = Issue.visible(user).all
93 issues = Issue.visible(user).all
94 assert issues.any?
94 assert issues.any?
95 assert_nil issues.detect {|issue| issue.project_id != 2}
95 assert_nil issues.detect {|issue| issue.project_id != 2}
96 end
96 end
97
97
98 def test_visible_scope_for_admin
98 def test_visible_scope_for_admin
99 user = User.find(1)
99 user = User.find(1)
100 user.members.each(&:destroy)
100 user.members.each(&:destroy)
101 assert user.projects.empty?
101 assert user.projects.empty?
102 issues = Issue.visible(user).all
102 issues = Issue.visible(user).all
103 assert issues.any?
103 assert issues.any?
104 # Admin should see issues on private projects that he does not belong to
104 # Admin should see issues on private projects that he does not belong to
105 assert issues.detect {|issue| !issue.project.is_public?}
105 assert issues.detect {|issue| !issue.project.is_public?}
106 end
106 end
107
107
108 def test_errors_full_messages_should_include_custom_fields_errors
108 def test_errors_full_messages_should_include_custom_fields_errors
109 field = IssueCustomField.find_by_name('Database')
109 field = IssueCustomField.find_by_name('Database')
110
110
111 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'test_create', :description => 'IssueTest#test_create_with_required_custom_field')
111 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'test_create', :description => 'IssueTest#test_create_with_required_custom_field')
112 assert issue.available_custom_fields.include?(field)
112 assert issue.available_custom_fields.include?(field)
113 # Invalid value
113 # Invalid value
114 issue.custom_field_values = { field.id => 'SQLServer' }
114 issue.custom_field_values = { field.id => 'SQLServer' }
115
115
116 assert !issue.valid?
116 assert !issue.valid?
117 assert_equal 1, issue.errors.full_messages.size
117 assert_equal 1, issue.errors.full_messages.size
118 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}", issue.errors.full_messages.first
118 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}", issue.errors.full_messages.first
119 end
119 end
120
120
121 def test_update_issue_with_required_custom_field
121 def test_update_issue_with_required_custom_field
122 field = IssueCustomField.find_by_name('Database')
122 field = IssueCustomField.find_by_name('Database')
123 field.update_attribute(:is_required, true)
123 field.update_attribute(:is_required, true)
124
124
125 issue = Issue.find(1)
125 issue = Issue.find(1)
126 assert_nil issue.custom_value_for(field)
126 assert_nil issue.custom_value_for(field)
127 assert issue.available_custom_fields.include?(field)
127 assert issue.available_custom_fields.include?(field)
128 # No change to custom values, issue can be saved
128 # No change to custom values, issue can be saved
129 assert issue.save
129 assert issue.save
130 # Blank value
130 # Blank value
131 issue.custom_field_values = { field.id => '' }
131 issue.custom_field_values = { field.id => '' }
132 assert !issue.save
132 assert !issue.save
133 # Valid value
133 # Valid value
134 issue.custom_field_values = { field.id => 'PostgreSQL' }
134 issue.custom_field_values = { field.id => 'PostgreSQL' }
135 assert issue.save
135 assert issue.save
136 issue.reload
136 issue.reload
137 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
137 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
138 end
138 end
139
139
140 def test_should_not_update_attributes_if_custom_fields_validation_fails
140 def test_should_not_update_attributes_if_custom_fields_validation_fails
141 issue = Issue.find(1)
141 issue = Issue.find(1)
142 field = IssueCustomField.find_by_name('Database')
142 field = IssueCustomField.find_by_name('Database')
143 assert issue.available_custom_fields.include?(field)
143 assert issue.available_custom_fields.include?(field)
144
144
145 issue.custom_field_values = { field.id => 'Invalid' }
145 issue.custom_field_values = { field.id => 'Invalid' }
146 issue.subject = 'Should be not be saved'
146 issue.subject = 'Should be not be saved'
147 assert !issue.save
147 assert !issue.save
148
148
149 issue.reload
149 issue.reload
150 assert_equal "Can't print recipes", issue.subject
150 assert_equal "Can't print recipes", issue.subject
151 end
151 end
152
152
153 def test_should_not_recreate_custom_values_objects_on_update
153 def test_should_not_recreate_custom_values_objects_on_update
154 field = IssueCustomField.find_by_name('Database')
154 field = IssueCustomField.find_by_name('Database')
155
155
156 issue = Issue.find(1)
156 issue = Issue.find(1)
157 issue.custom_field_values = { field.id => 'PostgreSQL' }
157 issue.custom_field_values = { field.id => 'PostgreSQL' }
158 assert issue.save
158 assert issue.save
159 custom_value = issue.custom_value_for(field)
159 custom_value = issue.custom_value_for(field)
160 issue.reload
160 issue.reload
161 issue.custom_field_values = { field.id => 'MySQL' }
161 issue.custom_field_values = { field.id => 'MySQL' }
162 assert issue.save
162 assert issue.save
163 issue.reload
163 issue.reload
164 assert_equal custom_value.id, issue.custom_value_for(field).id
164 assert_equal custom_value.id, issue.custom_value_for(field).id
165 end
165 end
166
166
167 def test_assigning_tracker_id_should_reload_custom_fields_values
167 def test_assigning_tracker_id_should_reload_custom_fields_values
168 issue = Issue.new(:project => Project.find(1))
168 issue = Issue.new(:project => Project.find(1))
169 assert issue.custom_field_values.empty?
169 assert issue.custom_field_values.empty?
170 issue.tracker_id = 1
170 issue.tracker_id = 1
171 assert issue.custom_field_values.any?
171 assert issue.custom_field_values.any?
172 end
172 end
173
173
174 def test_assigning_attributes_should_assign_tracker_id_first
174 def test_assigning_attributes_should_assign_tracker_id_first
175 attributes = ActiveSupport::OrderedHash.new
175 attributes = ActiveSupport::OrderedHash.new
176 attributes['custom_field_values'] = { '1' => 'MySQL' }
176 attributes['custom_field_values'] = { '1' => 'MySQL' }
177 attributes['tracker_id'] = '1'
177 attributes['tracker_id'] = '1'
178 issue = Issue.new(:project => Project.find(1))
178 issue = Issue.new(:project => Project.find(1))
179 issue.attributes = attributes
179 issue.attributes = attributes
180 assert_not_nil issue.custom_value_for(1)
180 assert_not_nil issue.custom_value_for(1)
181 assert_equal 'MySQL', issue.custom_value_for(1).value
181 assert_equal 'MySQL', issue.custom_value_for(1).value
182 end
182 end
183
183
184 def test_should_update_issue_with_disabled_tracker
184 def test_should_update_issue_with_disabled_tracker
185 p = Project.find(1)
185 p = Project.find(1)
186 issue = Issue.find(1)
186 issue = Issue.find(1)
187
187
188 p.trackers.delete(issue.tracker)
188 p.trackers.delete(issue.tracker)
189 assert !p.trackers.include?(issue.tracker)
189 assert !p.trackers.include?(issue.tracker)
190
190
191 issue.reload
191 issue.reload
192 issue.subject = 'New subject'
192 issue.subject = 'New subject'
193 assert issue.save
193 assert issue.save
194 end
194 end
195
195
196 def test_should_not_set_a_disabled_tracker
196 def test_should_not_set_a_disabled_tracker
197 p = Project.find(1)
197 p = Project.find(1)
198 p.trackers.delete(Tracker.find(2))
198 p.trackers.delete(Tracker.find(2))
199
199
200 issue = Issue.find(1)
200 issue = Issue.find(1)
201 issue.tracker_id = 2
201 issue.tracker_id = 2
202 issue.subject = 'New subject'
202 issue.subject = 'New subject'
203 assert !issue.save
203 assert !issue.save
204 assert_not_nil issue.errors.on(:tracker_id)
204 assert_not_nil issue.errors.on(:tracker_id)
205 end
205 end
206
206
207 def test_category_based_assignment
207 def test_category_based_assignment
208 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1)
208 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1)
209 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
209 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
210 end
210 end
211
211
212 def test_copy
212 def test_copy
213 issue = Issue.new.copy_from(1)
213 issue = Issue.new.copy_from(1)
214 assert issue.save
214 assert issue.save
215 issue.reload
215 issue.reload
216 orig = Issue.find(1)
216 orig = Issue.find(1)
217 assert_equal orig.subject, issue.subject
217 assert_equal orig.subject, issue.subject
218 assert_equal orig.tracker, issue.tracker
218 assert_equal orig.tracker, issue.tracker
219 assert_equal orig.custom_values.first.value, issue.custom_values.first.value
219 assert_equal orig.custom_values.first.value, issue.custom_values.first.value
220 end
220 end
221
221
222 def test_copy_should_copy_status
222 def test_copy_should_copy_status
223 orig = Issue.find(8)
223 orig = Issue.find(8)
224 assert orig.status != IssueStatus.default
224 assert orig.status != IssueStatus.default
225
225
226 issue = Issue.new.copy_from(orig)
226 issue = Issue.new.copy_from(orig)
227 assert issue.save
227 assert issue.save
228 issue.reload
228 issue.reload
229 assert_equal orig.status, issue.status
229 assert_equal orig.status, issue.status
230 end
230 end
231
231
232 def test_should_close_duplicates
232 def test_should_close_duplicates
233 # Create 3 issues
233 # Create 3 issues
234 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test')
234 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test')
235 assert issue1.save
235 assert issue1.save
236 issue2 = issue1.clone
236 issue2 = issue1.clone
237 assert issue2.save
237 assert issue2.save
238 issue3 = issue1.clone
238 issue3 = issue1.clone
239 assert issue3.save
239 assert issue3.save
240
240
241 # 2 is a dupe of 1
241 # 2 is a dupe of 1
242 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
242 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
243 # And 3 is a dupe of 2
243 # And 3 is a dupe of 2
244 IssueRelation.create(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
244 IssueRelation.create(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
245 # And 3 is a dupe of 1 (circular duplicates)
245 # And 3 is a dupe of 1 (circular duplicates)
246 IssueRelation.create(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
246 IssueRelation.create(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
247
247
248 assert issue1.reload.duplicates.include?(issue2)
248 assert issue1.reload.duplicates.include?(issue2)
249
249
250 # Closing issue 1
250 # Closing issue 1
251 issue1.init_journal(User.find(:first), "Closing issue1")
251 issue1.init_journal(User.find(:first), "Closing issue1")
252 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
252 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
253 assert issue1.save
253 assert issue1.save
254 # 2 and 3 should be also closed
254 # 2 and 3 should be also closed
255 assert issue2.reload.closed?
255 assert issue2.reload.closed?
256 assert issue3.reload.closed?
256 assert issue3.reload.closed?
257 end
257 end
258
258
259 def test_should_not_close_duplicated_issue
259 def test_should_not_close_duplicated_issue
260 # Create 3 issues
260 # Create 3 issues
261 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test')
261 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'Duplicates test', :description => 'Duplicates test')
262 assert issue1.save
262 assert issue1.save
263 issue2 = issue1.clone
263 issue2 = issue1.clone
264 assert issue2.save
264 assert issue2.save
265
265
266 # 2 is a dupe of 1
266 # 2 is a dupe of 1
267 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
267 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
268 # 2 is a dup of 1 but 1 is not a duplicate of 2
268 # 2 is a dup of 1 but 1 is not a duplicate of 2
269 assert !issue2.reload.duplicates.include?(issue1)
269 assert !issue2.reload.duplicates.include?(issue1)
270
270
271 # Closing issue 2
271 # Closing issue 2
272 issue2.init_journal(User.find(:first), "Closing issue2")
272 issue2.init_journal(User.find(:first), "Closing issue2")
273 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
273 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
274 assert issue2.save
274 assert issue2.save
275 # 1 should not be also closed
275 # 1 should not be also closed
276 assert !issue1.reload.closed?
276 assert !issue1.reload.closed?
277 end
277 end
278
278
279 def test_assignable_versions
279 def test_assignable_versions
280 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
280 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
281 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
281 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
282 end
282 end
283
283
284 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
284 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
285 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
285 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
286 assert !issue.save
286 assert !issue.save
287 assert_not_nil issue.errors.on(:fixed_version_id)
287 assert_not_nil issue.errors.on(:fixed_version_id)
288 end
288 end
289
289
290 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
290 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
291 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
291 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
292 assert !issue.save
292 assert !issue.save
293 assert_not_nil issue.errors.on(:fixed_version_id)
293 assert_not_nil issue.errors.on(:fixed_version_id)
294 end
294 end
295
295
296 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
296 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
297 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
297 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
298 assert issue.save
298 assert issue.save
299 end
299 end
300
300
301 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
301 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
302 issue = Issue.find(11)
302 issue = Issue.find(11)
303 assert_equal 'closed', issue.fixed_version.status
303 assert_equal 'closed', issue.fixed_version.status
304 issue.subject = 'Subject changed'
304 issue.subject = 'Subject changed'
305 assert issue.save
305 assert issue.save
306 end
306 end
307
307
308 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
308 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
309 issue = Issue.find(11)
309 issue = Issue.find(11)
310 issue.status_id = 1
310 issue.status_id = 1
311 assert !issue.save
311 assert !issue.save
312 assert_not_nil issue.errors.on_base
312 assert_not_nil issue.errors.on_base
313 end
313 end
314
314
315 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
315 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
316 issue = Issue.find(11)
316 issue = Issue.find(11)
317 issue.status_id = 1
317 issue.status_id = 1
318 issue.fixed_version_id = 3
318 issue.fixed_version_id = 3
319 assert issue.save
319 assert issue.save
320 end
320 end
321
321
322 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
322 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
323 issue = Issue.find(12)
323 issue = Issue.find(12)
324 assert_equal 'locked', issue.fixed_version.status
324 assert_equal 'locked', issue.fixed_version.status
325 issue.status_id = 1
325 issue.status_id = 1
326 assert issue.save
326 assert issue.save
327 end
327 end
328
328
329 def test_move_to_another_project_with_same_category
329 def test_move_to_another_project_with_same_category
330 issue = Issue.find(1)
330 issue = Issue.find(1)
331 assert issue.move_to(Project.find(2))
331 assert issue.move_to(Project.find(2))
332 issue.reload
332 issue.reload
333 assert_equal 2, issue.project_id
333 assert_equal 2, issue.project_id
334 # Category changes
334 # Category changes
335 assert_equal 4, issue.category_id
335 assert_equal 4, issue.category_id
336 # Make sure time entries were move to the target project
336 # Make sure time entries were move to the target project
337 assert_equal 2, issue.time_entries.first.project_id
337 assert_equal 2, issue.time_entries.first.project_id
338 end
338 end
339
339
340 def test_move_to_another_project_without_same_category
340 def test_move_to_another_project_without_same_category
341 issue = Issue.find(2)
341 issue = Issue.find(2)
342 assert issue.move_to(Project.find(2))
342 assert issue.move_to(Project.find(2))
343 issue.reload
343 issue.reload
344 assert_equal 2, issue.project_id
344 assert_equal 2, issue.project_id
345 # Category cleared
345 # Category cleared
346 assert_nil issue.category_id
346 assert_nil issue.category_id
347 end
347 end
348
348
349 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
349 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
350 issue = Issue.find(1)
350 issue = Issue.find(1)
351 issue.update_attribute(:fixed_version_id, 1)
351 issue.update_attribute(:fixed_version_id, 1)
352 assert issue.move_to(Project.find(2))
352 assert issue.move_to(Project.find(2))
353 issue.reload
353 issue.reload
354 assert_equal 2, issue.project_id
354 assert_equal 2, issue.project_id
355 # Cleared fixed_version
355 # Cleared fixed_version
356 assert_equal nil, issue.fixed_version
356 assert_equal nil, issue.fixed_version
357 end
357 end
358
358
359 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
359 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
360 issue = Issue.find(1)
360 issue = Issue.find(1)
361 issue.update_attribute(:fixed_version_id, 4)
361 issue.update_attribute(:fixed_version_id, 4)
362 assert issue.move_to(Project.find(5))
362 assert issue.move_to(Project.find(5))
363 issue.reload
363 issue.reload
364 assert_equal 5, issue.project_id
364 assert_equal 5, issue.project_id
365 # Keep fixed_version
365 # Keep fixed_version
366 assert_equal 4, issue.fixed_version_id
366 assert_equal 4, issue.fixed_version_id
367 end
367 end
368
368
369 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
369 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
370 issue = Issue.find(1)
370 issue = Issue.find(1)
371 issue.update_attribute(:fixed_version_id, 1)
371 issue.update_attribute(:fixed_version_id, 1)
372 assert issue.move_to(Project.find(5))
372 assert issue.move_to(Project.find(5))
373 issue.reload
373 issue.reload
374 assert_equal 5, issue.project_id
374 assert_equal 5, issue.project_id
375 # Cleared fixed_version
375 # Cleared fixed_version
376 assert_equal nil, issue.fixed_version
376 assert_equal nil, issue.fixed_version
377 end
377 end
378
378
379 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
379 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
380 issue = Issue.find(1)
380 issue = Issue.find(1)
381 issue.update_attribute(:fixed_version_id, 7)
381 issue.update_attribute(:fixed_version_id, 7)
382 assert issue.move_to(Project.find(2))
382 assert issue.move_to(Project.find(2))
383 issue.reload
383 issue.reload
384 assert_equal 2, issue.project_id
384 assert_equal 2, issue.project_id
385 # Keep fixed_version
385 # Keep fixed_version
386 assert_equal 7, issue.fixed_version_id
386 assert_equal 7, issue.fixed_version_id
387 end
387 end
388
388
389 def test_copy_to_the_same_project
389 def test_copy_to_the_same_project
390 issue = Issue.find(1)
390 issue = Issue.find(1)
391 copy = nil
391 copy = nil
392 assert_difference 'Issue.count' do
392 assert_difference 'Issue.count' do
393 copy = issue.move_to(issue.project, nil, :copy => true)
393 copy = issue.move_to(issue.project, nil, :copy => true)
394 end
394 end
395 assert_kind_of Issue, copy
395 assert_kind_of Issue, copy
396 assert_equal issue.project, copy.project
396 assert_equal issue.project, copy.project
397 assert_equal "125", copy.custom_value_for(2).value
397 assert_equal "125", copy.custom_value_for(2).value
398 end
398 end
399
399
400 def test_copy_to_another_project_and_tracker
400 def test_copy_to_another_project_and_tracker
401 issue = Issue.find(1)
401 issue = Issue.find(1)
402 copy = nil
402 copy = nil
403 assert_difference 'Issue.count' do
403 assert_difference 'Issue.count' do
404 copy = issue.move_to(Project.find(3), Tracker.find(2), :copy => true)
404 copy = issue.move_to(Project.find(3), Tracker.find(2), :copy => true)
405 end
405 end
406 assert_kind_of Issue, copy
406 assert_kind_of Issue, copy
407 assert_equal Project.find(3), copy.project
407 assert_equal Project.find(3), copy.project
408 assert_equal Tracker.find(2), copy.tracker
408 assert_equal Tracker.find(2), copy.tracker
409 # Custom field #2 is not associated with target tracker
409 # Custom field #2 is not associated with target tracker
410 assert_nil copy.custom_value_for(2)
410 assert_nil copy.custom_value_for(2)
411 end
411 end
412
412
413 context "#move_to" do
413 context "#move_to" do
414 context "as a copy" do
414 context "as a copy" do
415 setup do
415 setup do
416 @issue = Issue.find(1)
416 @issue = Issue.find(1)
417 @copy = nil
417 @copy = nil
418 end
418 end
419
419
420 should "allow assigned_to changes" do
420 should "allow assigned_to changes" do
421 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:assigned_to_id => 3}})
421 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:assigned_to_id => 3}})
422 assert_equal 3, @copy.assigned_to_id
422 assert_equal 3, @copy.assigned_to_id
423 end
423 end
424
424
425 should "allow status changes" do
425 should "allow status changes" do
426 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:status_id => 2}})
426 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:status_id => 2}})
427 assert_equal 2, @copy.status_id
427 assert_equal 2, @copy.status_id
428 end
428 end
429
429
430 should "allow start date changes" do
430 should "allow start date changes" do
431 date = Date.today
431 date = Date.today
432 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:start_date => date}})
432 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:start_date => date}})
433 assert_equal date, @copy.start_date
433 assert_equal date, @copy.start_date
434 end
434 end
435
435
436 should "allow due date changes" do
436 should "allow due date changes" do
437 date = Date.today
437 date = Date.today
438 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:due_date => date}})
438 @copy = @issue.move_to(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:due_date => date}})
439
439
440 assert_equal date, @copy.due_date
440 assert_equal date, @copy.due_date
441 end
441 end
442 end
442 end
443 end
443 end
444
444
445 def test_recipients_should_not_include_users_that_cannot_view_the_issue
445 def test_recipients_should_not_include_users_that_cannot_view_the_issue
446 issue = Issue.find(12)
446 issue = Issue.find(12)
447 assert issue.recipients.include?(issue.author.mail)
447 assert issue.recipients.include?(issue.author.mail)
448 # move the issue to a private project
448 # move the issue to a private project
449 copy = issue.move_to(Project.find(5), Tracker.find(2), :copy => true)
449 copy = issue.move_to(Project.find(5), Tracker.find(2), :copy => true)
450 # author is not a member of project anymore
450 # author is not a member of project anymore
451 assert !copy.recipients.include?(copy.author.mail)
451 assert !copy.recipients.include?(copy.author.mail)
452 end
452 end
453
453
454 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
454 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
455 user = User.find(3)
455 user = User.find(3)
456 issue = Issue.find(9)
456 issue = Issue.find(9)
457 Watcher.create!(:user => user, :watchable => issue)
457 Watcher.create!(:user => user, :watchable => issue)
458 assert issue.watched_by?(user)
458 assert issue.watched_by?(user)
459 assert !issue.watcher_recipients.include?(user.mail)
459 assert !issue.watcher_recipients.include?(user.mail)
460 end
460 end
461
461
462 def test_issue_destroy
462 def test_issue_destroy
463 Issue.find(1).destroy
463 Issue.find(1).destroy
464 assert_nil Issue.find_by_id(1)
464 assert_nil Issue.find_by_id(1)
465 assert_nil TimeEntry.find_by_issue_id(1)
465 assert_nil TimeEntry.find_by_issue_id(1)
466 end
466 end
467
467
468 def test_blocked
468 def test_blocked
469 blocked_issue = Issue.find(9)
469 blocked_issue = Issue.find(9)
470 blocking_issue = Issue.find(10)
470 blocking_issue = Issue.find(10)
471
471
472 assert blocked_issue.blocked?
472 assert blocked_issue.blocked?
473 assert !blocking_issue.blocked?
473 assert !blocking_issue.blocked?
474 end
474 end
475
475
476 def test_blocked_issues_dont_allow_closed_statuses
476 def test_blocked_issues_dont_allow_closed_statuses
477 blocked_issue = Issue.find(9)
477 blocked_issue = Issue.find(9)
478
478
479 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
479 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
480 assert !allowed_statuses.empty?
480 assert !allowed_statuses.empty?
481 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
481 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
482 assert closed_statuses.empty?
482 assert closed_statuses.empty?
483 end
483 end
484
484
485 def test_unblocked_issues_allow_closed_statuses
485 def test_unblocked_issues_allow_closed_statuses
486 blocking_issue = Issue.find(10)
486 blocking_issue = Issue.find(10)
487
487
488 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
488 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
489 assert !allowed_statuses.empty?
489 assert !allowed_statuses.empty?
490 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
490 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
491 assert !closed_statuses.empty?
491 assert !closed_statuses.empty?
492 end
492 end
493
493
494 def test_overdue
494 def test_overdue
495 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
495 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
496 assert !Issue.new(:due_date => Date.today).overdue?
496 assert !Issue.new(:due_date => Date.today).overdue?
497 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
497 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
498 assert !Issue.new(:due_date => nil).overdue?
498 assert !Issue.new(:due_date => nil).overdue?
499 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
499 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
500 end
500 end
501
501
502 def test_assignable_users
502 def test_assignable_users
503 assert_kind_of User, Issue.find(1).assignable_users.first
503 assert_kind_of User, Issue.find(1).assignable_users.first
504 end
504 end
505
505
506 def test_create_should_send_email_notification
506 def test_create_should_send_email_notification
507 ActionMailer::Base.deliveries.clear
507 ActionMailer::Base.deliveries.clear
508 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :estimated_hours => '1:30')
508 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :estimated_hours => '1:30')
509
509
510 assert issue.save
510 assert issue.save
511 assert_equal 1, ActionMailer::Base.deliveries.size
511 assert_equal 1, ActionMailer::Base.deliveries.size
512 end
512 end
513
513
514 def test_stale_issue_should_not_send_email_notification
514 def test_stale_issue_should_not_send_email_notification
515 ActionMailer::Base.deliveries.clear
515 ActionMailer::Base.deliveries.clear
516 issue = Issue.find(1)
516 issue = Issue.find(1)
517 stale = Issue.find(1)
517 stale = Issue.find(1)
518
518
519 issue.init_journal(User.find(1))
519 issue.init_journal(User.find(1))
520 issue.subject = 'Subjet update'
520 issue.subject = 'Subjet update'
521 assert issue.save
521 assert issue.save
522 assert_equal 1, ActionMailer::Base.deliveries.size
522 assert_equal 1, ActionMailer::Base.deliveries.size
523 ActionMailer::Base.deliveries.clear
523 ActionMailer::Base.deliveries.clear
524
524
525 stale.init_journal(User.find(1))
525 stale.init_journal(User.find(1))
526 stale.subject = 'Another subjet update'
526 stale.subject = 'Another subjet update'
527 assert_raise ActiveRecord::StaleObjectError do
527 assert_raise ActiveRecord::StaleObjectError do
528 stale.save
528 stale.save
529 end
529 end
530 assert ActionMailer::Base.deliveries.empty?
530 assert ActionMailer::Base.deliveries.empty?
531 end
531 end
532
533 context "#done_ratio" do
534 setup do
535 @issue = Issue.find(1)
536 @issue_status = IssueStatus.find(1)
537 @issue_status.update_attribute(:default_done_ratio, 50)
538 end
539
540 context "with Setting.issue_done_ratio using the issue_field" do
541 setup do
542 Setting.issue_done_ratio = 'issue_field'
543 end
544
545 should "read the issue's field" do
546 assert_equal 0, @issue.done_ratio
547 end
548 end
549
550 context "with Setting.issue_done_ratio using the issue_status" do
551 setup do
552 Setting.issue_done_ratio = 'issue_status'
553 end
554
555 should "read the Issue Status's default done ratio" do
556 assert_equal 50, @issue.done_ratio
557 end
558 end
559 end
560
561 context "#update_done_ratio_from_issue_status" do
562 setup do
563 @issue = Issue.find(1)
564 @issue_status = IssueStatus.find(1)
565 @issue_status.update_attribute(:default_done_ratio, 50)
566 end
567
568 context "with Setting.issue_done_ratio using the issue_field" do
569 setup do
570 Setting.issue_done_ratio = 'issue_field'
571 end
572
573 should "not change the issue" do
574 @issue.update_done_ratio_from_issue_status
575
576 assert_equal 0, @issue.done_ratio
577 end
578 end
579
580 context "with Setting.issue_done_ratio using the issue_status" do
581 setup do
582 Setting.issue_done_ratio = 'issue_status'
583 end
584
585 should "not change the issue's done ratio" do
586 @issue.update_done_ratio_from_issue_status
587
588 assert_equal 50, @issue.done_ratio
589 end
590 end
591 end
532 end
592 end
General Comments 0
You need to be logged in to leave comments. Login now