##// END OF EJS Templates
Add a default mail notification setting for new users...
Eric Davis -
r4105:582ed86d828b
parent child
Show More
@@ -1,460 +1,460
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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 "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < Principal
20 class User < Principal
21
21
22 # Account statuses
22 # Account statuses
23 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
24 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
25 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
26 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
27
27
28 USER_FORMATS = {
28 USER_FORMATS = {
29 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :username => '#{login}'
33 :username => '#{login}'
34 }
34 }
35
35
36 MAIL_NOTIFICATION_OPTIONS = [
36 MAIL_NOTIFICATION_OPTIONS = [
37 [:all, :label_user_mail_option_all],
37 [:all, :label_user_mail_option_all],
38 [:selected, :label_user_mail_option_selected],
38 [:selected, :label_user_mail_option_selected],
39 [:none, :label_user_mail_option_none],
39 [:none, :label_user_mail_option_none],
40 [:only_my_events, :label_user_mail_option_only_my_events],
40 [:only_my_events, :label_user_mail_option_only_my_events],
41 [:only_assigned, :label_user_mail_option_only_assigned],
41 [:only_assigned, :label_user_mail_option_only_assigned],
42 [:only_owner, :label_user_mail_option_only_owner]
42 [:only_owner, :label_user_mail_option_only_owner]
43 ]
43 ]
44
44
45 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
45 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
46 :after_remove => Proc.new {|user, group| group.user_removed(user)}
46 :after_remove => Proc.new {|user, group| group.user_removed(user)}
47 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
47 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
48 has_many :changesets, :dependent => :nullify
48 has_many :changesets, :dependent => :nullify
49 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
49 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
50 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
50 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
51 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
51 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
52 belongs_to :auth_source
52 belongs_to :auth_source
53
53
54 # Active non-anonymous users scope
54 # Active non-anonymous users scope
55 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
55 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
56
56
57 acts_as_customizable
57 acts_as_customizable
58
58
59 attr_accessor :password, :password_confirmation
59 attr_accessor :password, :password_confirmation
60 attr_accessor :last_before_login_on
60 attr_accessor :last_before_login_on
61 # Prevents unauthorized assignments
61 # Prevents unauthorized assignments
62 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
62 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
63
63
64 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
64 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
65 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
65 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
66 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
66 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
67 # Login must contain lettres, numbers, underscores only
67 # Login must contain lettres, numbers, underscores only
68 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
68 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
69 validates_length_of :login, :maximum => 30
69 validates_length_of :login, :maximum => 30
70 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
70 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
71 validates_length_of :firstname, :lastname, :maximum => 30
71 validates_length_of :firstname, :lastname, :maximum => 30
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
75
75
76 def before_create
76 def before_create
77 self.mail_notification = 'only_my_events'
77 self.mail_notification = Setting.default_notification_option
78 true
78 true
79 end
79 end
80
80
81 def before_save
81 def before_save
82 # update hashed_password if password was set
82 # update hashed_password if password was set
83 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
83 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
84 end
84 end
85
85
86 def reload(*args)
86 def reload(*args)
87 @name = nil
87 @name = nil
88 super
88 super
89 end
89 end
90
90
91 def mail=(arg)
91 def mail=(arg)
92 write_attribute(:mail, arg.to_s.strip)
92 write_attribute(:mail, arg.to_s.strip)
93 end
93 end
94
94
95 def identity_url=(url)
95 def identity_url=(url)
96 if url.blank?
96 if url.blank?
97 write_attribute(:identity_url, '')
97 write_attribute(:identity_url, '')
98 else
98 else
99 begin
99 begin
100 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
100 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
101 rescue OpenIdAuthentication::InvalidOpenId
101 rescue OpenIdAuthentication::InvalidOpenId
102 # Invlaid url, don't save
102 # Invlaid url, don't save
103 end
103 end
104 end
104 end
105 self.read_attribute(:identity_url)
105 self.read_attribute(:identity_url)
106 end
106 end
107
107
108 # Returns the user that matches provided login and password, or nil
108 # Returns the user that matches provided login and password, or nil
109 def self.try_to_login(login, password)
109 def self.try_to_login(login, password)
110 # Make sure no one can sign in with an empty password
110 # Make sure no one can sign in with an empty password
111 return nil if password.to_s.empty?
111 return nil if password.to_s.empty?
112 user = find_by_login(login)
112 user = find_by_login(login)
113 if user
113 if user
114 # user is already in local database
114 # user is already in local database
115 return nil if !user.active?
115 return nil if !user.active?
116 if user.auth_source
116 if user.auth_source
117 # user has an external authentication method
117 # user has an external authentication method
118 return nil unless user.auth_source.authenticate(login, password)
118 return nil unless user.auth_source.authenticate(login, password)
119 else
119 else
120 # authentication with local password
120 # authentication with local password
121 return nil unless User.hash_password(password) == user.hashed_password
121 return nil unless User.hash_password(password) == user.hashed_password
122 end
122 end
123 else
123 else
124 # user is not yet registered, try to authenticate with available sources
124 # user is not yet registered, try to authenticate with available sources
125 attrs = AuthSource.authenticate(login, password)
125 attrs = AuthSource.authenticate(login, password)
126 if attrs
126 if attrs
127 user = new(attrs)
127 user = new(attrs)
128 user.login = login
128 user.login = login
129 user.language = Setting.default_language
129 user.language = Setting.default_language
130 if user.save
130 if user.save
131 user.reload
131 user.reload
132 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
132 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
133 end
133 end
134 end
134 end
135 end
135 end
136 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
136 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
137 user
137 user
138 rescue => text
138 rescue => text
139 raise text
139 raise text
140 end
140 end
141
141
142 # Returns the user who matches the given autologin +key+ or nil
142 # Returns the user who matches the given autologin +key+ or nil
143 def self.try_to_autologin(key)
143 def self.try_to_autologin(key)
144 tokens = Token.find_all_by_action_and_value('autologin', key)
144 tokens = Token.find_all_by_action_and_value('autologin', key)
145 # Make sure there's only 1 token that matches the key
145 # Make sure there's only 1 token that matches the key
146 if tokens.size == 1
146 if tokens.size == 1
147 token = tokens.first
147 token = tokens.first
148 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
148 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
149 token.user.update_attribute(:last_login_on, Time.now)
149 token.user.update_attribute(:last_login_on, Time.now)
150 token.user
150 token.user
151 end
151 end
152 end
152 end
153 end
153 end
154
154
155 # Return user's full name for display
155 # Return user's full name for display
156 def name(formatter = nil)
156 def name(formatter = nil)
157 if formatter
157 if formatter
158 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
158 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
159 else
159 else
160 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
160 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
161 end
161 end
162 end
162 end
163
163
164 def active?
164 def active?
165 self.status == STATUS_ACTIVE
165 self.status == STATUS_ACTIVE
166 end
166 end
167
167
168 def registered?
168 def registered?
169 self.status == STATUS_REGISTERED
169 self.status == STATUS_REGISTERED
170 end
170 end
171
171
172 def locked?
172 def locked?
173 self.status == STATUS_LOCKED
173 self.status == STATUS_LOCKED
174 end
174 end
175
175
176 def activate
176 def activate
177 self.status = STATUS_ACTIVE
177 self.status = STATUS_ACTIVE
178 end
178 end
179
179
180 def register
180 def register
181 self.status = STATUS_REGISTERED
181 self.status = STATUS_REGISTERED
182 end
182 end
183
183
184 def lock
184 def lock
185 self.status = STATUS_LOCKED
185 self.status = STATUS_LOCKED
186 end
186 end
187
187
188 def activate!
188 def activate!
189 update_attribute(:status, STATUS_ACTIVE)
189 update_attribute(:status, STATUS_ACTIVE)
190 end
190 end
191
191
192 def register!
192 def register!
193 update_attribute(:status, STATUS_REGISTERED)
193 update_attribute(:status, STATUS_REGISTERED)
194 end
194 end
195
195
196 def lock!
196 def lock!
197 update_attribute(:status, STATUS_LOCKED)
197 update_attribute(:status, STATUS_LOCKED)
198 end
198 end
199
199
200 def check_password?(clear_password)
200 def check_password?(clear_password)
201 if auth_source_id.present?
201 if auth_source_id.present?
202 auth_source.authenticate(self.login, clear_password)
202 auth_source.authenticate(self.login, clear_password)
203 else
203 else
204 User.hash_password(clear_password) == self.hashed_password
204 User.hash_password(clear_password) == self.hashed_password
205 end
205 end
206 end
206 end
207
207
208 # Does the backend storage allow this user to change their password?
208 # Does the backend storage allow this user to change their password?
209 def change_password_allowed?
209 def change_password_allowed?
210 return true if auth_source_id.blank?
210 return true if auth_source_id.blank?
211 return auth_source.allow_password_changes?
211 return auth_source.allow_password_changes?
212 end
212 end
213
213
214 # Generate and set a random password. Useful for automated user creation
214 # Generate and set a random password. Useful for automated user creation
215 # Based on Token#generate_token_value
215 # Based on Token#generate_token_value
216 #
216 #
217 def random_password
217 def random_password
218 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
218 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
219 password = ''
219 password = ''
220 40.times { |i| password << chars[rand(chars.size-1)] }
220 40.times { |i| password << chars[rand(chars.size-1)] }
221 self.password = password
221 self.password = password
222 self.password_confirmation = password
222 self.password_confirmation = password
223 self
223 self
224 end
224 end
225
225
226 def pref
226 def pref
227 self.preference ||= UserPreference.new(:user => self)
227 self.preference ||= UserPreference.new(:user => self)
228 end
228 end
229
229
230 def time_zone
230 def time_zone
231 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
231 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
232 end
232 end
233
233
234 def wants_comments_in_reverse_order?
234 def wants_comments_in_reverse_order?
235 self.pref[:comments_sorting] == 'desc'
235 self.pref[:comments_sorting] == 'desc'
236 end
236 end
237
237
238 # Return user's RSS key (a 40 chars long string), used to access feeds
238 # Return user's RSS key (a 40 chars long string), used to access feeds
239 def rss_key
239 def rss_key
240 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
240 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
241 token.value
241 token.value
242 end
242 end
243
243
244 # Return user's API key (a 40 chars long string), used to access the API
244 # Return user's API key (a 40 chars long string), used to access the API
245 def api_key
245 def api_key
246 token = self.api_token || self.create_api_token(:action => 'api')
246 token = self.api_token || self.create_api_token(:action => 'api')
247 token.value
247 token.value
248 end
248 end
249
249
250 # Return an array of project ids for which the user has explicitly turned mail notifications on
250 # Return an array of project ids for which the user has explicitly turned mail notifications on
251 def notified_projects_ids
251 def notified_projects_ids
252 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
252 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
253 end
253 end
254
254
255 def notified_project_ids=(ids)
255 def notified_project_ids=(ids)
256 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
256 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
257 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
257 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
258 @notified_projects_ids = nil
258 @notified_projects_ids = nil
259 notified_projects_ids
259 notified_projects_ids
260 end
260 end
261
261
262 # Find a user account by matching the exact login and then a case-insensitive
262 # Find a user account by matching the exact login and then a case-insensitive
263 # version. Exact matches will be given priority.
263 # version. Exact matches will be given priority.
264 def self.find_by_login(login)
264 def self.find_by_login(login)
265 # force string comparison to be case sensitive on MySQL
265 # force string comparison to be case sensitive on MySQL
266 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
266 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
267
267
268 # First look for an exact match
268 # First look for an exact match
269 user = first(:conditions => ["#{type_cast} login = ?", login])
269 user = first(:conditions => ["#{type_cast} login = ?", login])
270 # Fail over to case-insensitive if none was found
270 # Fail over to case-insensitive if none was found
271 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
271 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
272 end
272 end
273
273
274 def self.find_by_rss_key(key)
274 def self.find_by_rss_key(key)
275 token = Token.find_by_value(key)
275 token = Token.find_by_value(key)
276 token && token.user.active? ? token.user : nil
276 token && token.user.active? ? token.user : nil
277 end
277 end
278
278
279 def self.find_by_api_key(key)
279 def self.find_by_api_key(key)
280 token = Token.find_by_action_and_value('api', key)
280 token = Token.find_by_action_and_value('api', key)
281 token && token.user.active? ? token.user : nil
281 token && token.user.active? ? token.user : nil
282 end
282 end
283
283
284 # Makes find_by_mail case-insensitive
284 # Makes find_by_mail case-insensitive
285 def self.find_by_mail(mail)
285 def self.find_by_mail(mail)
286 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
286 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
287 end
287 end
288
288
289 def to_s
289 def to_s
290 name
290 name
291 end
291 end
292
292
293 # Returns the current day according to user's time zone
293 # Returns the current day according to user's time zone
294 def today
294 def today
295 if time_zone.nil?
295 if time_zone.nil?
296 Date.today
296 Date.today
297 else
297 else
298 Time.now.in_time_zone(time_zone).to_date
298 Time.now.in_time_zone(time_zone).to_date
299 end
299 end
300 end
300 end
301
301
302 def logged?
302 def logged?
303 true
303 true
304 end
304 end
305
305
306 def anonymous?
306 def anonymous?
307 !logged?
307 !logged?
308 end
308 end
309
309
310 # Return user's roles for project
310 # Return user's roles for project
311 def roles_for_project(project)
311 def roles_for_project(project)
312 roles = []
312 roles = []
313 # No role on archived projects
313 # No role on archived projects
314 return roles unless project && project.active?
314 return roles unless project && project.active?
315 if logged?
315 if logged?
316 # Find project membership
316 # Find project membership
317 membership = memberships.detect {|m| m.project_id == project.id}
317 membership = memberships.detect {|m| m.project_id == project.id}
318 if membership
318 if membership
319 roles = membership.roles
319 roles = membership.roles
320 else
320 else
321 @role_non_member ||= Role.non_member
321 @role_non_member ||= Role.non_member
322 roles << @role_non_member
322 roles << @role_non_member
323 end
323 end
324 else
324 else
325 @role_anonymous ||= Role.anonymous
325 @role_anonymous ||= Role.anonymous
326 roles << @role_anonymous
326 roles << @role_anonymous
327 end
327 end
328 roles
328 roles
329 end
329 end
330
330
331 # Return true if the user is a member of project
331 # Return true if the user is a member of project
332 def member_of?(project)
332 def member_of?(project)
333 !roles_for_project(project).detect {|role| role.member?}.nil?
333 !roles_for_project(project).detect {|role| role.member?}.nil?
334 end
334 end
335
335
336 # Return true if the user is allowed to do the specified action on project
336 # Return true if the user is allowed to do the specified action on project
337 # action can be:
337 # action can be:
338 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
338 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
339 # * a permission Symbol (eg. :edit_project)
339 # * a permission Symbol (eg. :edit_project)
340 def allowed_to?(action, project, options={})
340 def allowed_to?(action, project, options={})
341 if project
341 if project
342 # No action allowed on archived projects
342 # No action allowed on archived projects
343 return false unless project.active?
343 return false unless project.active?
344 # No action allowed on disabled modules
344 # No action allowed on disabled modules
345 return false unless project.allows_to?(action)
345 return false unless project.allows_to?(action)
346 # Admin users are authorized for anything else
346 # Admin users are authorized for anything else
347 return true if admin?
347 return true if admin?
348
348
349 roles = roles_for_project(project)
349 roles = roles_for_project(project)
350 return false unless roles
350 return false unless roles
351 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
351 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
352
352
353 elsif options[:global]
353 elsif options[:global]
354 # Admin users are always authorized
354 # Admin users are always authorized
355 return true if admin?
355 return true if admin?
356
356
357 # authorize if user has at least one role that has this permission
357 # authorize if user has at least one role that has this permission
358 roles = memberships.collect {|m| m.roles}.flatten.uniq
358 roles = memberships.collect {|m| m.roles}.flatten.uniq
359 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
359 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
360 else
360 else
361 false
361 false
362 end
362 end
363 end
363 end
364
364
365 # Is the user allowed to do the specified action on any project?
365 # Is the user allowed to do the specified action on any project?
366 # See allowed_to? for the actions and valid options.
366 # See allowed_to? for the actions and valid options.
367 def allowed_to_globally?(action, options)
367 def allowed_to_globally?(action, options)
368 allowed_to?(action, nil, options.reverse_merge(:global => true))
368 allowed_to?(action, nil, options.reverse_merge(:global => true))
369 end
369 end
370
370
371 # Utility method to help check if a user should be notified about an
371 # Utility method to help check if a user should be notified about an
372 # event.
372 # event.
373 #
373 #
374 # TODO: only supports Issue events currently
374 # TODO: only supports Issue events currently
375 def notify_about?(object)
375 def notify_about?(object)
376 case mail_notification.to_sym
376 case mail_notification.to_sym
377 when :all
377 when :all
378 true
378 true
379 when :selected
379 when :selected
380 # Handled by the Project
380 # Handled by the Project
381 when :none
381 when :none
382 false
382 false
383 when :only_my_events
383 when :only_my_events
384 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
384 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
385 true
385 true
386 else
386 else
387 false
387 false
388 end
388 end
389 when :only_assigned
389 when :only_assigned
390 if object.is_a?(Issue) && object.assigned_to == self
390 if object.is_a?(Issue) && object.assigned_to == self
391 true
391 true
392 else
392 else
393 false
393 false
394 end
394 end
395 when :only_owner
395 when :only_owner
396 if object.is_a?(Issue) && object.author == self
396 if object.is_a?(Issue) && object.author == self
397 true
397 true
398 else
398 else
399 false
399 false
400 end
400 end
401 else
401 else
402 false
402 false
403 end
403 end
404 end
404 end
405
405
406 def self.current=(user)
406 def self.current=(user)
407 @current_user = user
407 @current_user = user
408 end
408 end
409
409
410 def self.current
410 def self.current
411 @current_user ||= User.anonymous
411 @current_user ||= User.anonymous
412 end
412 end
413
413
414 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
414 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
415 # one anonymous user per database.
415 # one anonymous user per database.
416 def self.anonymous
416 def self.anonymous
417 anonymous_user = AnonymousUser.find(:first)
417 anonymous_user = AnonymousUser.find(:first)
418 if anonymous_user.nil?
418 if anonymous_user.nil?
419 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
419 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
420 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
420 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
421 end
421 end
422 anonymous_user
422 anonymous_user
423 end
423 end
424
424
425 protected
425 protected
426
426
427 def validate
427 def validate
428 # Password length validation based on setting
428 # Password length validation based on setting
429 if !password.nil? && password.size < Setting.password_min_length.to_i
429 if !password.nil? && password.size < Setting.password_min_length.to_i
430 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
430 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
431 end
431 end
432 end
432 end
433
433
434 private
434 private
435
435
436 # Return password digest
436 # Return password digest
437 def self.hash_password(clear_password)
437 def self.hash_password(clear_password)
438 Digest::SHA1.hexdigest(clear_password || "")
438 Digest::SHA1.hexdigest(clear_password || "")
439 end
439 end
440 end
440 end
441
441
442 class AnonymousUser < User
442 class AnonymousUser < User
443
443
444 def validate_on_create
444 def validate_on_create
445 # There should be only one AnonymousUser in the database
445 # There should be only one AnonymousUser in the database
446 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
446 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
447 end
447 end
448
448
449 def available_custom_fields
449 def available_custom_fields
450 []
450 []
451 end
451 end
452
452
453 # Overrides a few properties
453 # Overrides a few properties
454 def logged?; false end
454 def logged?; false end
455 def admin; false end
455 def admin; false end
456 def name(*args); I18n.t(:label_user_anonymous) end
456 def name(*args); I18n.t(:label_user_anonymous) end
457 def mail; nil end
457 def mail; nil end
458 def time_zone; nil end
458 def time_zone; nil end
459 def rss_key; nil end
459 def rss_key; nil end
460 end
460 end
@@ -1,33 +1,36
1 <% if @deliveries %>
1 <% if @deliveries %>
2 <% form_tag({:action => 'edit', :tab => 'notifications'}) do %>
2 <% form_tag({:action => 'edit', :tab => 'notifications'}) do %>
3
3
4 <div class="box tabular settings">
4 <div class="box tabular settings">
5 <p><%= setting_text_field :mail_from, :size => 60 %></p>
5 <p><%= setting_text_field :mail_from, :size => 60 %></p>
6
6
7 <p><%= setting_check_box :bcc_recipients %></p>
7 <p><%= setting_check_box :bcc_recipients %></p>
8
8
9 <p><%= setting_check_box :plain_text_mail %></p>
9 <p><%= setting_check_box :plain_text_mail %></p>
10
11 <p><%= setting_select(:default_notification_option, User::MAIL_NOTIFICATION_OPTIONS.collect {|o| [l(o.last), o.first.to_s]}) %></p>
12
10 </div>
13 </div>
11
14
12 <fieldset class="box settings" id="notified_events"><legend><%=l(:text_select_mail_notifications)%></legend>
15 <fieldset class="box settings" id="notified_events"><legend><%=l(:text_select_mail_notifications)%></legend>
13 <%= setting_multiselect(:notified_events,
16 <%= setting_multiselect(:notified_events,
14 @notifiables.collect {|notifiable| [l_or_humanize(notifiable, :prefix => 'label_'), notifiable]}, :label => false) %>
17 @notifiables.collect {|notifiable| [l_or_humanize(notifiable, :prefix => 'label_'), notifiable]}, :label => false) %>
15
18
16 <p><%= check_all_links('notified_events') %></p>
19 <p><%= check_all_links('notified_events') %></p>
17 </fieldset>
20 </fieldset>
18
21
19 <fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
22 <fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
20 <%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5 %>
23 <%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5 %>
21 </fieldset>
24 </fieldset>
22
25
23 <div style="float:right;">
26 <div style="float:right;">
24 <%= link_to l(:label_send_test_email), :controller => 'admin', :action => 'test_email' %>
27 <%= link_to l(:label_send_test_email), :controller => 'admin', :action => 'test_email' %>
25 </div>
28 </div>
26
29
27 <%= submit_tag l(:button_save) %>
30 <%= submit_tag l(:button_save) %>
28 <% end %>
31 <% end %>
29 <% else %>
32 <% else %>
30 <div class="nodata">
33 <div class="nodata">
31 <%= simple_format(l(:text_email_delivery_not_configured)) %>
34 <%= simple_format(l(:text_email_delivery_not_configured)) %>
32 </div>
35 </div>
33 <% end %>
36 <% end %>
@@ -1,923 +1,924
1 en:
1 en:
2 # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
2 # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
3 direction: ltr
3 direction: ltr
4 date:
4 date:
5 formats:
5 formats:
6 # Use the strftime parameters for formats.
6 # Use the strftime parameters for formats.
7 # When no format has been given, it uses default.
7 # When no format has been given, it uses default.
8 # You can provide other formats here if you like!
8 # You can provide other formats here if you like!
9 default: "%m/%d/%Y"
9 default: "%m/%d/%Y"
10 short: "%b %d"
10 short: "%b %d"
11 long: "%B %d, %Y"
11 long: "%B %d, %Y"
12
12
13 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
13 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
14 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
14 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
15
15
16 # Don't forget the nil at the beginning; there's no such thing as a 0th month
16 # Don't forget the nil at the beginning; there's no such thing as a 0th month
17 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
17 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
18 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
18 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
19 # Used in date_select and datime_select.
19 # Used in date_select and datime_select.
20 order: [ :year, :month, :day ]
20 order: [ :year, :month, :day ]
21
21
22 time:
22 time:
23 formats:
23 formats:
24 default: "%m/%d/%Y %I:%M %p"
24 default: "%m/%d/%Y %I:%M %p"
25 time: "%I:%M %p"
25 time: "%I:%M %p"
26 short: "%d %b %H:%M"
26 short: "%d %b %H:%M"
27 long: "%B %d, %Y %H:%M"
27 long: "%B %d, %Y %H:%M"
28 am: "am"
28 am: "am"
29 pm: "pm"
29 pm: "pm"
30
30
31 datetime:
31 datetime:
32 distance_in_words:
32 distance_in_words:
33 half_a_minute: "half a minute"
33 half_a_minute: "half a minute"
34 less_than_x_seconds:
34 less_than_x_seconds:
35 one: "less than 1 second"
35 one: "less than 1 second"
36 other: "less than {{count}} seconds"
36 other: "less than {{count}} seconds"
37 x_seconds:
37 x_seconds:
38 one: "1 second"
38 one: "1 second"
39 other: "{{count}} seconds"
39 other: "{{count}} seconds"
40 less_than_x_minutes:
40 less_than_x_minutes:
41 one: "less than a minute"
41 one: "less than a minute"
42 other: "less than {{count}} minutes"
42 other: "less than {{count}} minutes"
43 x_minutes:
43 x_minutes:
44 one: "1 minute"
44 one: "1 minute"
45 other: "{{count}} minutes"
45 other: "{{count}} minutes"
46 about_x_hours:
46 about_x_hours:
47 one: "about 1 hour"
47 one: "about 1 hour"
48 other: "about {{count}} hours"
48 other: "about {{count}} hours"
49 x_days:
49 x_days:
50 one: "1 day"
50 one: "1 day"
51 other: "{{count}} days"
51 other: "{{count}} days"
52 about_x_months:
52 about_x_months:
53 one: "about 1 month"
53 one: "about 1 month"
54 other: "about {{count}} months"
54 other: "about {{count}} months"
55 x_months:
55 x_months:
56 one: "1 month"
56 one: "1 month"
57 other: "{{count}} months"
57 other: "{{count}} months"
58 about_x_years:
58 about_x_years:
59 one: "about 1 year"
59 one: "about 1 year"
60 other: "about {{count}} years"
60 other: "about {{count}} years"
61 over_x_years:
61 over_x_years:
62 one: "over 1 year"
62 one: "over 1 year"
63 other: "over {{count}} years"
63 other: "over {{count}} years"
64 almost_x_years:
64 almost_x_years:
65 one: "almost 1 year"
65 one: "almost 1 year"
66 other: "almost {{count}} years"
66 other: "almost {{count}} years"
67
67
68 number:
68 number:
69 # Default format for numbers
69 # Default format for numbers
70 format:
70 format:
71 separator: "."
71 separator: "."
72 delimiter: ""
72 delimiter: ""
73 precision: 3
73 precision: 3
74 human:
74 human:
75 format:
75 format:
76 delimiter: ""
76 delimiter: ""
77 precision: 1
77 precision: 1
78 storage_units:
78 storage_units:
79 format: "%n %u"
79 format: "%n %u"
80 units:
80 units:
81 byte:
81 byte:
82 one: "Byte"
82 one: "Byte"
83 other: "Bytes"
83 other: "Bytes"
84 kb: "KB"
84 kb: "KB"
85 mb: "MB"
85 mb: "MB"
86 gb: "GB"
86 gb: "GB"
87 tb: "TB"
87 tb: "TB"
88
88
89
89
90 # Used in array.to_sentence.
90 # Used in array.to_sentence.
91 support:
91 support:
92 array:
92 array:
93 sentence_connector: "and"
93 sentence_connector: "and"
94 skip_last_comma: false
94 skip_last_comma: false
95
95
96 activerecord:
96 activerecord:
97 errors:
97 errors:
98 messages:
98 messages:
99 inclusion: "is not included in the list"
99 inclusion: "is not included in the list"
100 exclusion: "is reserved"
100 exclusion: "is reserved"
101 invalid: "is invalid"
101 invalid: "is invalid"
102 confirmation: "doesn't match confirmation"
102 confirmation: "doesn't match confirmation"
103 accepted: "must be accepted"
103 accepted: "must be accepted"
104 empty: "can't be empty"
104 empty: "can't be empty"
105 blank: "can't be blank"
105 blank: "can't be blank"
106 too_long: "is too long (maximum is {{count}} characters)"
106 too_long: "is too long (maximum is {{count}} characters)"
107 too_short: "is too short (minimum is {{count}} characters)"
107 too_short: "is too short (minimum is {{count}} characters)"
108 wrong_length: "is the wrong length (should be {{count}} characters)"
108 wrong_length: "is the wrong length (should be {{count}} characters)"
109 taken: "has already been taken"
109 taken: "has already been taken"
110 not_a_number: "is not a number"
110 not_a_number: "is not a number"
111 not_a_date: "is not a valid date"
111 not_a_date: "is not a valid date"
112 greater_than: "must be greater than {{count}}"
112 greater_than: "must be greater than {{count}}"
113 greater_than_or_equal_to: "must be greater than or equal to {{count}}"
113 greater_than_or_equal_to: "must be greater than or equal to {{count}}"
114 equal_to: "must be equal to {{count}}"
114 equal_to: "must be equal to {{count}}"
115 less_than: "must be less than {{count}}"
115 less_than: "must be less than {{count}}"
116 less_than_or_equal_to: "must be less than or equal to {{count}}"
116 less_than_or_equal_to: "must be less than or equal to {{count}}"
117 odd: "must be odd"
117 odd: "must be odd"
118 even: "must be even"
118 even: "must be even"
119 greater_than_start_date: "must be greater than start date"
119 greater_than_start_date: "must be greater than start date"
120 not_same_project: "doesn't belong to the same project"
120 not_same_project: "doesn't belong to the same project"
121 circular_dependency: "This relation would create a circular dependency"
121 circular_dependency: "This relation would create a circular dependency"
122 cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
122 cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
123
123
124 actionview_instancetag_blank_option: Please select
124 actionview_instancetag_blank_option: Please select
125
125
126 general_text_No: 'No'
126 general_text_No: 'No'
127 general_text_Yes: 'Yes'
127 general_text_Yes: 'Yes'
128 general_text_no: 'no'
128 general_text_no: 'no'
129 general_text_yes: 'yes'
129 general_text_yes: 'yes'
130 general_lang_name: 'English'
130 general_lang_name: 'English'
131 general_csv_separator: ','
131 general_csv_separator: ','
132 general_csv_decimal_separator: '.'
132 general_csv_decimal_separator: '.'
133 general_csv_encoding: ISO-8859-1
133 general_csv_encoding: ISO-8859-1
134 general_pdf_encoding: ISO-8859-1
134 general_pdf_encoding: ISO-8859-1
135 general_first_day_of_week: '7'
135 general_first_day_of_week: '7'
136
136
137 notice_account_updated: Account was successfully updated.
137 notice_account_updated: Account was successfully updated.
138 notice_account_invalid_creditentials: Invalid user or password
138 notice_account_invalid_creditentials: Invalid user or password
139 notice_account_password_updated: Password was successfully updated.
139 notice_account_password_updated: Password was successfully updated.
140 notice_account_wrong_password: Wrong password
140 notice_account_wrong_password: Wrong password
141 notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
141 notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
142 notice_account_unknown_email: Unknown user.
142 notice_account_unknown_email: Unknown user.
143 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
143 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
144 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
144 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
145 notice_account_activated: Your account has been activated. You can now log in.
145 notice_account_activated: Your account has been activated. You can now log in.
146 notice_successful_create: Successful creation.
146 notice_successful_create: Successful creation.
147 notice_successful_update: Successful update.
147 notice_successful_update: Successful update.
148 notice_successful_delete: Successful deletion.
148 notice_successful_delete: Successful deletion.
149 notice_successful_connection: Successful connection.
149 notice_successful_connection: Successful connection.
150 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
150 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
151 notice_locking_conflict: Data has been updated by another user.
151 notice_locking_conflict: Data has been updated by another user.
152 notice_not_authorized: You are not authorized to access this page.
152 notice_not_authorized: You are not authorized to access this page.
153 notice_email_sent: "An email was sent to {{value}}"
153 notice_email_sent: "An email was sent to {{value}}"
154 notice_email_error: "An error occurred while sending mail ({{value}})"
154 notice_email_error: "An error occurred while sending mail ({{value}})"
155 notice_feeds_access_key_reseted: Your RSS access key was reset.
155 notice_feeds_access_key_reseted: Your RSS access key was reset.
156 notice_api_access_key_reseted: Your API access key was reset.
156 notice_api_access_key_reseted: Your API access key was reset.
157 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
157 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
158 notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
158 notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
159 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
159 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
160 notice_account_pending: "Your account was created and is now pending administrator approval."
160 notice_account_pending: "Your account was created and is now pending administrator approval."
161 notice_default_data_loaded: Default configuration successfully loaded.
161 notice_default_data_loaded: Default configuration successfully loaded.
162 notice_unable_delete_version: Unable to delete version.
162 notice_unable_delete_version: Unable to delete version.
163 notice_unable_delete_time_entry: Unable to delete time log entry.
163 notice_unable_delete_time_entry: Unable to delete time log entry.
164 notice_issue_done_ratios_updated: Issue done ratios updated.
164 notice_issue_done_ratios_updated: Issue done ratios updated.
165
165
166 error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
166 error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
167 error_scm_not_found: "The entry or revision was not found in the repository."
167 error_scm_not_found: "The entry or revision was not found in the repository."
168 error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
168 error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
169 error_scm_annotate: "The entry does not exist or can not be annotated."
169 error_scm_annotate: "The entry does not exist or can not be annotated."
170 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
170 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
171 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
171 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
172 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
172 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
173 error_can_not_delete_custom_field: Unable to delete custom field
173 error_can_not_delete_custom_field: Unable to delete custom field
174 error_can_not_delete_tracker: "This tracker contains issues and can't be deleted."
174 error_can_not_delete_tracker: "This tracker contains issues and can't be deleted."
175 error_can_not_remove_role: "This role is in use and can not be deleted."
175 error_can_not_remove_role: "This role is in use and can not be deleted."
176 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
176 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
177 error_can_not_archive_project: This project can not be archived
177 error_can_not_archive_project: This project can not be archived
178 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
178 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
179 error_workflow_copy_source: 'Please select a source tracker or role'
179 error_workflow_copy_source: 'Please select a source tracker or role'
180 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
180 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
181 error_unable_delete_issue_status: 'Unable to delete issue status'
181 error_unable_delete_issue_status: 'Unable to delete issue status'
182 error_unable_to_connect: "Unable to connect ({{value}})"
182 error_unable_to_connect: "Unable to connect ({{value}})"
183 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
183 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
184
184
185 mail_subject_lost_password: "Your {{value}} password"
185 mail_subject_lost_password: "Your {{value}} password"
186 mail_body_lost_password: 'To change your password, click on the following link:'
186 mail_body_lost_password: 'To change your password, click on the following link:'
187 mail_subject_register: "Your {{value}} account activation"
187 mail_subject_register: "Your {{value}} account activation"
188 mail_body_register: 'To activate your account, click on the following link:'
188 mail_body_register: 'To activate your account, click on the following link:'
189 mail_body_account_information_external: "You can use your {{value}} account to log in."
189 mail_body_account_information_external: "You can use your {{value}} account to log in."
190 mail_body_account_information: Your account information
190 mail_body_account_information: Your account information
191 mail_subject_account_activation_request: "{{value}} account activation request"
191 mail_subject_account_activation_request: "{{value}} account activation request"
192 mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
192 mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
193 mail_subject_reminder: "{{count}} issue(s) due in the next {{days}} days"
193 mail_subject_reminder: "{{count}} issue(s) due in the next {{days}} days"
194 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
194 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
195 mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
195 mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
196 mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}."
196 mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}."
197 mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
197 mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
198 mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}."
198 mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}."
199
199
200 gui_validation_error: 1 error
200 gui_validation_error: 1 error
201 gui_validation_error_plural: "{{count}} errors"
201 gui_validation_error_plural: "{{count}} errors"
202
202
203 field_name: Name
203 field_name: Name
204 field_description: Description
204 field_description: Description
205 field_summary: Summary
205 field_summary: Summary
206 field_is_required: Required
206 field_is_required: Required
207 field_firstname: Firstname
207 field_firstname: Firstname
208 field_lastname: Lastname
208 field_lastname: Lastname
209 field_mail: Email
209 field_mail: Email
210 field_filename: File
210 field_filename: File
211 field_filesize: Size
211 field_filesize: Size
212 field_downloads: Downloads
212 field_downloads: Downloads
213 field_author: Author
213 field_author: Author
214 field_created_on: Created
214 field_created_on: Created
215 field_updated_on: Updated
215 field_updated_on: Updated
216 field_field_format: Format
216 field_field_format: Format
217 field_is_for_all: For all projects
217 field_is_for_all: For all projects
218 field_possible_values: Possible values
218 field_possible_values: Possible values
219 field_regexp: Regular expression
219 field_regexp: Regular expression
220 field_min_length: Minimum length
220 field_min_length: Minimum length
221 field_max_length: Maximum length
221 field_max_length: Maximum length
222 field_value: Value
222 field_value: Value
223 field_category: Category
223 field_category: Category
224 field_title: Title
224 field_title: Title
225 field_project: Project
225 field_project: Project
226 field_issue: Issue
226 field_issue: Issue
227 field_status: Status
227 field_status: Status
228 field_notes: Notes
228 field_notes: Notes
229 field_is_closed: Issue closed
229 field_is_closed: Issue closed
230 field_is_default: Default value
230 field_is_default: Default value
231 field_tracker: Tracker
231 field_tracker: Tracker
232 field_subject: Subject
232 field_subject: Subject
233 field_due_date: Due date
233 field_due_date: Due date
234 field_assigned_to: Assignee
234 field_assigned_to: Assignee
235 field_priority: Priority
235 field_priority: Priority
236 field_fixed_version: Target version
236 field_fixed_version: Target version
237 field_user: User
237 field_user: User
238 field_principal: Principal
238 field_principal: Principal
239 field_role: Role
239 field_role: Role
240 field_homepage: Homepage
240 field_homepage: Homepage
241 field_is_public: Public
241 field_is_public: Public
242 field_parent: Subproject of
242 field_parent: Subproject of
243 field_is_in_roadmap: Issues displayed in roadmap
243 field_is_in_roadmap: Issues displayed in roadmap
244 field_login: Login
244 field_login: Login
245 field_mail_notification: Email notifications
245 field_mail_notification: Email notifications
246 field_admin: Administrator
246 field_admin: Administrator
247 field_last_login_on: Last connection
247 field_last_login_on: Last connection
248 field_language: Language
248 field_language: Language
249 field_effective_date: Date
249 field_effective_date: Date
250 field_password: Password
250 field_password: Password
251 field_new_password: New password
251 field_new_password: New password
252 field_password_confirmation: Confirmation
252 field_password_confirmation: Confirmation
253 field_version: Version
253 field_version: Version
254 field_type: Type
254 field_type: Type
255 field_host: Host
255 field_host: Host
256 field_port: Port
256 field_port: Port
257 field_account: Account
257 field_account: Account
258 field_base_dn: Base DN
258 field_base_dn: Base DN
259 field_attr_login: Login attribute
259 field_attr_login: Login attribute
260 field_attr_firstname: Firstname attribute
260 field_attr_firstname: Firstname attribute
261 field_attr_lastname: Lastname attribute
261 field_attr_lastname: Lastname attribute
262 field_attr_mail: Email attribute
262 field_attr_mail: Email attribute
263 field_onthefly: On-the-fly user creation
263 field_onthefly: On-the-fly user creation
264 field_start_date: Start
264 field_start_date: Start
265 field_done_ratio: % Done
265 field_done_ratio: % Done
266 field_auth_source: Authentication mode
266 field_auth_source: Authentication mode
267 field_hide_mail: Hide my email address
267 field_hide_mail: Hide my email address
268 field_comments: Comment
268 field_comments: Comment
269 field_url: URL
269 field_url: URL
270 field_start_page: Start page
270 field_start_page: Start page
271 field_subproject: Subproject
271 field_subproject: Subproject
272 field_hours: Hours
272 field_hours: Hours
273 field_activity: Activity
273 field_activity: Activity
274 field_spent_on: Date
274 field_spent_on: Date
275 field_identifier: Identifier
275 field_identifier: Identifier
276 field_is_filter: Used as a filter
276 field_is_filter: Used as a filter
277 field_issue_to: Related issue
277 field_issue_to: Related issue
278 field_delay: Delay
278 field_delay: Delay
279 field_assignable: Issues can be assigned to this role
279 field_assignable: Issues can be assigned to this role
280 field_redirect_existing_links: Redirect existing links
280 field_redirect_existing_links: Redirect existing links
281 field_estimated_hours: Estimated time
281 field_estimated_hours: Estimated time
282 field_column_names: Columns
282 field_column_names: Columns
283 field_time_entries: Log time
283 field_time_entries: Log time
284 field_time_zone: Time zone
284 field_time_zone: Time zone
285 field_searchable: Searchable
285 field_searchable: Searchable
286 field_default_value: Default value
286 field_default_value: Default value
287 field_comments_sorting: Display comments
287 field_comments_sorting: Display comments
288 field_parent_title: Parent page
288 field_parent_title: Parent page
289 field_editable: Editable
289 field_editable: Editable
290 field_watcher: Watcher
290 field_watcher: Watcher
291 field_identity_url: OpenID URL
291 field_identity_url: OpenID URL
292 field_content: Content
292 field_content: Content
293 field_group_by: Group results by
293 field_group_by: Group results by
294 field_sharing: Sharing
294 field_sharing: Sharing
295 field_parent_issue: Parent task
295 field_parent_issue: Parent task
296 field_member_of_group: Member of Group
296 field_member_of_group: Member of Group
297 field_assigned_to_role: Member of Role
297 field_assigned_to_role: Member of Role
298 field_text: Text field
298 field_text: Text field
299
299
300 setting_app_title: Application title
300 setting_app_title: Application title
301 setting_app_subtitle: Application subtitle
301 setting_app_subtitle: Application subtitle
302 setting_welcome_text: Welcome text
302 setting_welcome_text: Welcome text
303 setting_default_language: Default language
303 setting_default_language: Default language
304 setting_login_required: Authentication required
304 setting_login_required: Authentication required
305 setting_self_registration: Self-registration
305 setting_self_registration: Self-registration
306 setting_attachment_max_size: Attachment max. size
306 setting_attachment_max_size: Attachment max. size
307 setting_issues_export_limit: Issues export limit
307 setting_issues_export_limit: Issues export limit
308 setting_mail_from: Emission email address
308 setting_mail_from: Emission email address
309 setting_bcc_recipients: Blind carbon copy recipients (bcc)
309 setting_bcc_recipients: Blind carbon copy recipients (bcc)
310 setting_plain_text_mail: Plain text mail (no HTML)
310 setting_plain_text_mail: Plain text mail (no HTML)
311 setting_host_name: Host name and path
311 setting_host_name: Host name and path
312 setting_text_formatting: Text formatting
312 setting_text_formatting: Text formatting
313 setting_wiki_compression: Wiki history compression
313 setting_wiki_compression: Wiki history compression
314 setting_feeds_limit: Feed content limit
314 setting_feeds_limit: Feed content limit
315 setting_default_projects_public: New projects are public by default
315 setting_default_projects_public: New projects are public by default
316 setting_autofetch_changesets: Autofetch commits
316 setting_autofetch_changesets: Autofetch commits
317 setting_sys_api_enabled: Enable WS for repository management
317 setting_sys_api_enabled: Enable WS for repository management
318 setting_commit_ref_keywords: Referencing keywords
318 setting_commit_ref_keywords: Referencing keywords
319 setting_commit_fix_keywords: Fixing keywords
319 setting_commit_fix_keywords: Fixing keywords
320 setting_autologin: Autologin
320 setting_autologin: Autologin
321 setting_date_format: Date format
321 setting_date_format: Date format
322 setting_time_format: Time format
322 setting_time_format: Time format
323 setting_cross_project_issue_relations: Allow cross-project issue relations
323 setting_cross_project_issue_relations: Allow cross-project issue relations
324 setting_issue_list_default_columns: Default columns displayed on the issue list
324 setting_issue_list_default_columns: Default columns displayed on the issue list
325 setting_repositories_encodings: Repositories encodings
325 setting_repositories_encodings: Repositories encodings
326 setting_commit_logs_encoding: Commit messages encoding
326 setting_commit_logs_encoding: Commit messages encoding
327 setting_emails_footer: Emails footer
327 setting_emails_footer: Emails footer
328 setting_protocol: Protocol
328 setting_protocol: Protocol
329 setting_per_page_options: Objects per page options
329 setting_per_page_options: Objects per page options
330 setting_user_format: Users display format
330 setting_user_format: Users display format
331 setting_activity_days_default: Days displayed on project activity
331 setting_activity_days_default: Days displayed on project activity
332 setting_display_subprojects_issues: Display subprojects issues on main projects by default
332 setting_display_subprojects_issues: Display subprojects issues on main projects by default
333 setting_enabled_scm: Enabled SCM
333 setting_enabled_scm: Enabled SCM
334 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
334 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
335 setting_mail_handler_api_enabled: Enable WS for incoming emails
335 setting_mail_handler_api_enabled: Enable WS for incoming emails
336 setting_mail_handler_api_key: API key
336 setting_mail_handler_api_key: API key
337 setting_sequential_project_identifiers: Generate sequential project identifiers
337 setting_sequential_project_identifiers: Generate sequential project identifiers
338 setting_gravatar_enabled: Use Gravatar user icons
338 setting_gravatar_enabled: Use Gravatar user icons
339 setting_gravatar_default: Default Gravatar image
339 setting_gravatar_default: Default Gravatar image
340 setting_diff_max_lines_displayed: Max number of diff lines displayed
340 setting_diff_max_lines_displayed: Max number of diff lines displayed
341 setting_file_max_size_displayed: Max size of text files displayed inline
341 setting_file_max_size_displayed: Max size of text files displayed inline
342 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
342 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
343 setting_openid: Allow OpenID login and registration
343 setting_openid: Allow OpenID login and registration
344 setting_password_min_length: Minimum password length
344 setting_password_min_length: Minimum password length
345 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
345 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
346 setting_default_projects_modules: Default enabled modules for new projects
346 setting_default_projects_modules: Default enabled modules for new projects
347 setting_issue_done_ratio: Calculate the issue done ratio with
347 setting_issue_done_ratio: Calculate the issue done ratio with
348 setting_issue_done_ratio_issue_field: Use the issue field
348 setting_issue_done_ratio_issue_field: Use the issue field
349 setting_issue_done_ratio_issue_status: Use the issue status
349 setting_issue_done_ratio_issue_status: Use the issue status
350 setting_start_of_week: Start calendars on
350 setting_start_of_week: Start calendars on
351 setting_rest_api_enabled: Enable REST web service
351 setting_rest_api_enabled: Enable REST web service
352 setting_cache_formatted_text: Cache formatted text
352 setting_cache_formatted_text: Cache formatted text
353 setting_default_notification_option: Default notification option
353
354
354 permission_add_project: Create project
355 permission_add_project: Create project
355 permission_add_subprojects: Create subprojects
356 permission_add_subprojects: Create subprojects
356 permission_edit_project: Edit project
357 permission_edit_project: Edit project
357 permission_select_project_modules: Select project modules
358 permission_select_project_modules: Select project modules
358 permission_manage_members: Manage members
359 permission_manage_members: Manage members
359 permission_manage_project_activities: Manage project activities
360 permission_manage_project_activities: Manage project activities
360 permission_manage_versions: Manage versions
361 permission_manage_versions: Manage versions
361 permission_manage_categories: Manage issue categories
362 permission_manage_categories: Manage issue categories
362 permission_view_issues: View Issues
363 permission_view_issues: View Issues
363 permission_add_issues: Add issues
364 permission_add_issues: Add issues
364 permission_edit_issues: Edit issues
365 permission_edit_issues: Edit issues
365 permission_manage_issue_relations: Manage issue relations
366 permission_manage_issue_relations: Manage issue relations
366 permission_add_issue_notes: Add notes
367 permission_add_issue_notes: Add notes
367 permission_edit_issue_notes: Edit notes
368 permission_edit_issue_notes: Edit notes
368 permission_edit_own_issue_notes: Edit own notes
369 permission_edit_own_issue_notes: Edit own notes
369 permission_move_issues: Move issues
370 permission_move_issues: Move issues
370 permission_delete_issues: Delete issues
371 permission_delete_issues: Delete issues
371 permission_manage_public_queries: Manage public queries
372 permission_manage_public_queries: Manage public queries
372 permission_save_queries: Save queries
373 permission_save_queries: Save queries
373 permission_view_gantt: View gantt chart
374 permission_view_gantt: View gantt chart
374 permission_view_calendar: View calendar
375 permission_view_calendar: View calendar
375 permission_view_issue_watchers: View watchers list
376 permission_view_issue_watchers: View watchers list
376 permission_add_issue_watchers: Add watchers
377 permission_add_issue_watchers: Add watchers
377 permission_delete_issue_watchers: Delete watchers
378 permission_delete_issue_watchers: Delete watchers
378 permission_log_time: Log spent time
379 permission_log_time: Log spent time
379 permission_view_time_entries: View spent time
380 permission_view_time_entries: View spent time
380 permission_edit_time_entries: Edit time logs
381 permission_edit_time_entries: Edit time logs
381 permission_edit_own_time_entries: Edit own time logs
382 permission_edit_own_time_entries: Edit own time logs
382 permission_manage_news: Manage news
383 permission_manage_news: Manage news
383 permission_comment_news: Comment news
384 permission_comment_news: Comment news
384 permission_manage_documents: Manage documents
385 permission_manage_documents: Manage documents
385 permission_view_documents: View documents
386 permission_view_documents: View documents
386 permission_manage_files: Manage files
387 permission_manage_files: Manage files
387 permission_view_files: View files
388 permission_view_files: View files
388 permission_manage_wiki: Manage wiki
389 permission_manage_wiki: Manage wiki
389 permission_rename_wiki_pages: Rename wiki pages
390 permission_rename_wiki_pages: Rename wiki pages
390 permission_delete_wiki_pages: Delete wiki pages
391 permission_delete_wiki_pages: Delete wiki pages
391 permission_view_wiki_pages: View wiki
392 permission_view_wiki_pages: View wiki
392 permission_view_wiki_edits: View wiki history
393 permission_view_wiki_edits: View wiki history
393 permission_edit_wiki_pages: Edit wiki pages
394 permission_edit_wiki_pages: Edit wiki pages
394 permission_delete_wiki_pages_attachments: Delete attachments
395 permission_delete_wiki_pages_attachments: Delete attachments
395 permission_protect_wiki_pages: Protect wiki pages
396 permission_protect_wiki_pages: Protect wiki pages
396 permission_manage_repository: Manage repository
397 permission_manage_repository: Manage repository
397 permission_browse_repository: Browse repository
398 permission_browse_repository: Browse repository
398 permission_view_changesets: View changesets
399 permission_view_changesets: View changesets
399 permission_commit_access: Commit access
400 permission_commit_access: Commit access
400 permission_manage_boards: Manage boards
401 permission_manage_boards: Manage boards
401 permission_view_messages: View messages
402 permission_view_messages: View messages
402 permission_add_messages: Post messages
403 permission_add_messages: Post messages
403 permission_edit_messages: Edit messages
404 permission_edit_messages: Edit messages
404 permission_edit_own_messages: Edit own messages
405 permission_edit_own_messages: Edit own messages
405 permission_delete_messages: Delete messages
406 permission_delete_messages: Delete messages
406 permission_delete_own_messages: Delete own messages
407 permission_delete_own_messages: Delete own messages
407 permission_export_wiki_pages: Export wiki pages
408 permission_export_wiki_pages: Export wiki pages
408 permission_manage_subtasks: Manage subtasks
409 permission_manage_subtasks: Manage subtasks
409
410
410 project_module_issue_tracking: Issue tracking
411 project_module_issue_tracking: Issue tracking
411 project_module_time_tracking: Time tracking
412 project_module_time_tracking: Time tracking
412 project_module_news: News
413 project_module_news: News
413 project_module_documents: Documents
414 project_module_documents: Documents
414 project_module_files: Files
415 project_module_files: Files
415 project_module_wiki: Wiki
416 project_module_wiki: Wiki
416 project_module_repository: Repository
417 project_module_repository: Repository
417 project_module_boards: Boards
418 project_module_boards: Boards
418 project_module_calendar: Calendar
419 project_module_calendar: Calendar
419 project_module_gantt: Gantt
420 project_module_gantt: Gantt
420
421
421 label_user: User
422 label_user: User
422 label_user_plural: Users
423 label_user_plural: Users
423 label_user_new: New user
424 label_user_new: New user
424 label_user_anonymous: Anonymous
425 label_user_anonymous: Anonymous
425 label_project: Project
426 label_project: Project
426 label_project_new: New project
427 label_project_new: New project
427 label_project_plural: Projects
428 label_project_plural: Projects
428 label_x_projects:
429 label_x_projects:
429 zero: no projects
430 zero: no projects
430 one: 1 project
431 one: 1 project
431 other: "{{count}} projects"
432 other: "{{count}} projects"
432 label_project_all: All Projects
433 label_project_all: All Projects
433 label_project_latest: Latest projects
434 label_project_latest: Latest projects
434 label_issue: Issue
435 label_issue: Issue
435 label_issue_new: New issue
436 label_issue_new: New issue
436 label_issue_plural: Issues
437 label_issue_plural: Issues
437 label_issue_view_all: View all issues
438 label_issue_view_all: View all issues
438 label_issues_by: "Issues by {{value}}"
439 label_issues_by: "Issues by {{value}}"
439 label_issue_added: Issue added
440 label_issue_added: Issue added
440 label_issue_updated: Issue updated
441 label_issue_updated: Issue updated
441 label_document: Document
442 label_document: Document
442 label_document_new: New document
443 label_document_new: New document
443 label_document_plural: Documents
444 label_document_plural: Documents
444 label_document_added: Document added
445 label_document_added: Document added
445 label_role: Role
446 label_role: Role
446 label_role_plural: Roles
447 label_role_plural: Roles
447 label_role_new: New role
448 label_role_new: New role
448 label_role_and_permissions: Roles and permissions
449 label_role_and_permissions: Roles and permissions
449 label_member: Member
450 label_member: Member
450 label_member_new: New member
451 label_member_new: New member
451 label_member_plural: Members
452 label_member_plural: Members
452 label_tracker: Tracker
453 label_tracker: Tracker
453 label_tracker_plural: Trackers
454 label_tracker_plural: Trackers
454 label_tracker_new: New tracker
455 label_tracker_new: New tracker
455 label_workflow: Workflow
456 label_workflow: Workflow
456 label_issue_status: Issue status
457 label_issue_status: Issue status
457 label_issue_status_plural: Issue statuses
458 label_issue_status_plural: Issue statuses
458 label_issue_status_new: New status
459 label_issue_status_new: New status
459 label_issue_category: Issue category
460 label_issue_category: Issue category
460 label_issue_category_plural: Issue categories
461 label_issue_category_plural: Issue categories
461 label_issue_category_new: New category
462 label_issue_category_new: New category
462 label_custom_field: Custom field
463 label_custom_field: Custom field
463 label_custom_field_plural: Custom fields
464 label_custom_field_plural: Custom fields
464 label_custom_field_new: New custom field
465 label_custom_field_new: New custom field
465 label_enumerations: Enumerations
466 label_enumerations: Enumerations
466 label_enumeration_new: New value
467 label_enumeration_new: New value
467 label_information: Information
468 label_information: Information
468 label_information_plural: Information
469 label_information_plural: Information
469 label_please_login: Please log in
470 label_please_login: Please log in
470 label_register: Register
471 label_register: Register
471 label_login_with_open_id_option: or login with OpenID
472 label_login_with_open_id_option: or login with OpenID
472 label_password_lost: Lost password
473 label_password_lost: Lost password
473 label_home: Home
474 label_home: Home
474 label_my_page: My page
475 label_my_page: My page
475 label_my_account: My account
476 label_my_account: My account
476 label_my_projects: My projects
477 label_my_projects: My projects
477 label_my_page_block: My page block
478 label_my_page_block: My page block
478 label_administration: Administration
479 label_administration: Administration
479 label_login: Sign in
480 label_login: Sign in
480 label_logout: Sign out
481 label_logout: Sign out
481 label_help: Help
482 label_help: Help
482 label_reported_issues: Reported issues
483 label_reported_issues: Reported issues
483 label_assigned_to_me_issues: Issues assigned to me
484 label_assigned_to_me_issues: Issues assigned to me
484 label_last_login: Last connection
485 label_last_login: Last connection
485 label_registered_on: Registered on
486 label_registered_on: Registered on
486 label_activity: Activity
487 label_activity: Activity
487 label_overall_activity: Overall activity
488 label_overall_activity: Overall activity
488 label_user_activity: "{{value}}'s activity"
489 label_user_activity: "{{value}}'s activity"
489 label_new: New
490 label_new: New
490 label_logged_as: Logged in as
491 label_logged_as: Logged in as
491 label_environment: Environment
492 label_environment: Environment
492 label_authentication: Authentication
493 label_authentication: Authentication
493 label_auth_source: Authentication mode
494 label_auth_source: Authentication mode
494 label_auth_source_new: New authentication mode
495 label_auth_source_new: New authentication mode
495 label_auth_source_plural: Authentication modes
496 label_auth_source_plural: Authentication modes
496 label_subproject_plural: Subprojects
497 label_subproject_plural: Subprojects
497 label_subproject_new: New subproject
498 label_subproject_new: New subproject
498 label_and_its_subprojects: "{{value}} and its subprojects"
499 label_and_its_subprojects: "{{value}} and its subprojects"
499 label_min_max_length: Min - Max length
500 label_min_max_length: Min - Max length
500 label_list: List
501 label_list: List
501 label_date: Date
502 label_date: Date
502 label_integer: Integer
503 label_integer: Integer
503 label_float: Float
504 label_float: Float
504 label_boolean: Boolean
505 label_boolean: Boolean
505 label_string: Text
506 label_string: Text
506 label_text: Long text
507 label_text: Long text
507 label_attribute: Attribute
508 label_attribute: Attribute
508 label_attribute_plural: Attributes
509 label_attribute_plural: Attributes
509 label_download: "{{count}} Download"
510 label_download: "{{count}} Download"
510 label_download_plural: "{{count}} Downloads"
511 label_download_plural: "{{count}} Downloads"
511 label_no_data: No data to display
512 label_no_data: No data to display
512 label_change_status: Change status
513 label_change_status: Change status
513 label_history: History
514 label_history: History
514 label_attachment: File
515 label_attachment: File
515 label_attachment_new: New file
516 label_attachment_new: New file
516 label_attachment_delete: Delete file
517 label_attachment_delete: Delete file
517 label_attachment_plural: Files
518 label_attachment_plural: Files
518 label_file_added: File added
519 label_file_added: File added
519 label_report: Report
520 label_report: Report
520 label_report_plural: Reports
521 label_report_plural: Reports
521 label_news: News
522 label_news: News
522 label_news_new: Add news
523 label_news_new: Add news
523 label_news_plural: News
524 label_news_plural: News
524 label_news_latest: Latest news
525 label_news_latest: Latest news
525 label_news_view_all: View all news
526 label_news_view_all: View all news
526 label_news_added: News added
527 label_news_added: News added
527 label_settings: Settings
528 label_settings: Settings
528 label_overview: Overview
529 label_overview: Overview
529 label_version: Version
530 label_version: Version
530 label_version_new: New version
531 label_version_new: New version
531 label_version_plural: Versions
532 label_version_plural: Versions
532 label_close_versions: Close completed versions
533 label_close_versions: Close completed versions
533 label_confirmation: Confirmation
534 label_confirmation: Confirmation
534 label_export_to: 'Also available in:'
535 label_export_to: 'Also available in:'
535 label_read: Read...
536 label_read: Read...
536 label_public_projects: Public projects
537 label_public_projects: Public projects
537 label_open_issues: open
538 label_open_issues: open
538 label_open_issues_plural: open
539 label_open_issues_plural: open
539 label_closed_issues: closed
540 label_closed_issues: closed
540 label_closed_issues_plural: closed
541 label_closed_issues_plural: closed
541 label_x_open_issues_abbr_on_total:
542 label_x_open_issues_abbr_on_total:
542 zero: 0 open / {{total}}
543 zero: 0 open / {{total}}
543 one: 1 open / {{total}}
544 one: 1 open / {{total}}
544 other: "{{count}} open / {{total}}"
545 other: "{{count}} open / {{total}}"
545 label_x_open_issues_abbr:
546 label_x_open_issues_abbr:
546 zero: 0 open
547 zero: 0 open
547 one: 1 open
548 one: 1 open
548 other: "{{count}} open"
549 other: "{{count}} open"
549 label_x_closed_issues_abbr:
550 label_x_closed_issues_abbr:
550 zero: 0 closed
551 zero: 0 closed
551 one: 1 closed
552 one: 1 closed
552 other: "{{count}} closed"
553 other: "{{count}} closed"
553 label_total: Total
554 label_total: Total
554 label_permissions: Permissions
555 label_permissions: Permissions
555 label_current_status: Current status
556 label_current_status: Current status
556 label_new_statuses_allowed: New statuses allowed
557 label_new_statuses_allowed: New statuses allowed
557 label_all: all
558 label_all: all
558 label_none: none
559 label_none: none
559 label_nobody: nobody
560 label_nobody: nobody
560 label_next: Next
561 label_next: Next
561 label_previous: Previous
562 label_previous: Previous
562 label_used_by: Used by
563 label_used_by: Used by
563 label_details: Details
564 label_details: Details
564 label_add_note: Add a note
565 label_add_note: Add a note
565 label_per_page: Per page
566 label_per_page: Per page
566 label_calendar: Calendar
567 label_calendar: Calendar
567 label_months_from: months from
568 label_months_from: months from
568 label_gantt: Gantt
569 label_gantt: Gantt
569 label_internal: Internal
570 label_internal: Internal
570 label_last_changes: "last {{count}} changes"
571 label_last_changes: "last {{count}} changes"
571 label_change_view_all: View all changes
572 label_change_view_all: View all changes
572 label_personalize_page: Personalize this page
573 label_personalize_page: Personalize this page
573 label_comment: Comment
574 label_comment: Comment
574 label_comment_plural: Comments
575 label_comment_plural: Comments
575 label_x_comments:
576 label_x_comments:
576 zero: no comments
577 zero: no comments
577 one: 1 comment
578 one: 1 comment
578 other: "{{count}} comments"
579 other: "{{count}} comments"
579 label_comment_add: Add a comment
580 label_comment_add: Add a comment
580 label_comment_added: Comment added
581 label_comment_added: Comment added
581 label_comment_delete: Delete comments
582 label_comment_delete: Delete comments
582 label_query: Custom query
583 label_query: Custom query
583 label_query_plural: Custom queries
584 label_query_plural: Custom queries
584 label_query_new: New query
585 label_query_new: New query
585 label_filter_add: Add filter
586 label_filter_add: Add filter
586 label_filter_plural: Filters
587 label_filter_plural: Filters
587 label_equals: is
588 label_equals: is
588 label_not_equals: is not
589 label_not_equals: is not
589 label_in_less_than: in less than
590 label_in_less_than: in less than
590 label_in_more_than: in more than
591 label_in_more_than: in more than
591 label_greater_or_equal: '>='
592 label_greater_or_equal: '>='
592 label_less_or_equal: '<='
593 label_less_or_equal: '<='
593 label_in: in
594 label_in: in
594 label_today: today
595 label_today: today
595 label_all_time: all time
596 label_all_time: all time
596 label_yesterday: yesterday
597 label_yesterday: yesterday
597 label_this_week: this week
598 label_this_week: this week
598 label_last_week: last week
599 label_last_week: last week
599 label_last_n_days: "last {{count}} days"
600 label_last_n_days: "last {{count}} days"
600 label_this_month: this month
601 label_this_month: this month
601 label_last_month: last month
602 label_last_month: last month
602 label_this_year: this year
603 label_this_year: this year
603 label_date_range: Date range
604 label_date_range: Date range
604 label_less_than_ago: less than days ago
605 label_less_than_ago: less than days ago
605 label_more_than_ago: more than days ago
606 label_more_than_ago: more than days ago
606 label_ago: days ago
607 label_ago: days ago
607 label_contains: contains
608 label_contains: contains
608 label_not_contains: doesn't contain
609 label_not_contains: doesn't contain
609 label_day_plural: days
610 label_day_plural: days
610 label_repository: Repository
611 label_repository: Repository
611 label_repository_plural: Repositories
612 label_repository_plural: Repositories
612 label_browse: Browse
613 label_browse: Browse
613 label_modification: "{{count}} change"
614 label_modification: "{{count}} change"
614 label_modification_plural: "{{count}} changes"
615 label_modification_plural: "{{count}} changes"
615 label_branch: Branch
616 label_branch: Branch
616 label_tag: Tag
617 label_tag: Tag
617 label_revision: Revision
618 label_revision: Revision
618 label_revision_plural: Revisions
619 label_revision_plural: Revisions
619 label_revision_id: "Revision {{value}}"
620 label_revision_id: "Revision {{value}}"
620 label_associated_revisions: Associated revisions
621 label_associated_revisions: Associated revisions
621 label_added: added
622 label_added: added
622 label_modified: modified
623 label_modified: modified
623 label_copied: copied
624 label_copied: copied
624 label_renamed: renamed
625 label_renamed: renamed
625 label_deleted: deleted
626 label_deleted: deleted
626 label_latest_revision: Latest revision
627 label_latest_revision: Latest revision
627 label_latest_revision_plural: Latest revisions
628 label_latest_revision_plural: Latest revisions
628 label_view_revisions: View revisions
629 label_view_revisions: View revisions
629 label_view_all_revisions: View all revisions
630 label_view_all_revisions: View all revisions
630 label_max_size: Maximum size
631 label_max_size: Maximum size
631 label_sort_highest: Move to top
632 label_sort_highest: Move to top
632 label_sort_higher: Move up
633 label_sort_higher: Move up
633 label_sort_lower: Move down
634 label_sort_lower: Move down
634 label_sort_lowest: Move to bottom
635 label_sort_lowest: Move to bottom
635 label_roadmap: Roadmap
636 label_roadmap: Roadmap
636 label_roadmap_due_in: "Due in {{value}}"
637 label_roadmap_due_in: "Due in {{value}}"
637 label_roadmap_overdue: "{{value}} late"
638 label_roadmap_overdue: "{{value}} late"
638 label_roadmap_no_issues: No issues for this version
639 label_roadmap_no_issues: No issues for this version
639 label_search: Search
640 label_search: Search
640 label_result_plural: Results
641 label_result_plural: Results
641 label_all_words: All words
642 label_all_words: All words
642 label_wiki: Wiki
643 label_wiki: Wiki
643 label_wiki_edit: Wiki edit
644 label_wiki_edit: Wiki edit
644 label_wiki_edit_plural: Wiki edits
645 label_wiki_edit_plural: Wiki edits
645 label_wiki_page: Wiki page
646 label_wiki_page: Wiki page
646 label_wiki_page_plural: Wiki pages
647 label_wiki_page_plural: Wiki pages
647 label_index_by_title: Index by title
648 label_index_by_title: Index by title
648 label_index_by_date: Index by date
649 label_index_by_date: Index by date
649 label_current_version: Current version
650 label_current_version: Current version
650 label_preview: Preview
651 label_preview: Preview
651 label_feed_plural: Feeds
652 label_feed_plural: Feeds
652 label_changes_details: Details of all changes
653 label_changes_details: Details of all changes
653 label_issue_tracking: Issue tracking
654 label_issue_tracking: Issue tracking
654 label_spent_time: Spent time
655 label_spent_time: Spent time
655 label_overall_spent_time: Overall spent time
656 label_overall_spent_time: Overall spent time
656 label_f_hour: "{{value}} hour"
657 label_f_hour: "{{value}} hour"
657 label_f_hour_plural: "{{value}} hours"
658 label_f_hour_plural: "{{value}} hours"
658 label_time_tracking: Time tracking
659 label_time_tracking: Time tracking
659 label_change_plural: Changes
660 label_change_plural: Changes
660 label_statistics: Statistics
661 label_statistics: Statistics
661 label_commits_per_month: Commits per month
662 label_commits_per_month: Commits per month
662 label_commits_per_author: Commits per author
663 label_commits_per_author: Commits per author
663 label_view_diff: View differences
664 label_view_diff: View differences
664 label_diff_inline: inline
665 label_diff_inline: inline
665 label_diff_side_by_side: side by side
666 label_diff_side_by_side: side by side
666 label_options: Options
667 label_options: Options
667 label_copy_workflow_from: Copy workflow from
668 label_copy_workflow_from: Copy workflow from
668 label_permissions_report: Permissions report
669 label_permissions_report: Permissions report
669 label_watched_issues: Watched issues
670 label_watched_issues: Watched issues
670 label_related_issues: Related issues
671 label_related_issues: Related issues
671 label_applied_status: Applied status
672 label_applied_status: Applied status
672 label_loading: Loading...
673 label_loading: Loading...
673 label_relation_new: New relation
674 label_relation_new: New relation
674 label_relation_delete: Delete relation
675 label_relation_delete: Delete relation
675 label_relates_to: related to
676 label_relates_to: related to
676 label_duplicates: duplicates
677 label_duplicates: duplicates
677 label_duplicated_by: duplicated by
678 label_duplicated_by: duplicated by
678 label_blocks: blocks
679 label_blocks: blocks
679 label_blocked_by: blocked by
680 label_blocked_by: blocked by
680 label_precedes: precedes
681 label_precedes: precedes
681 label_follows: follows
682 label_follows: follows
682 label_end_to_start: end to start
683 label_end_to_start: end to start
683 label_end_to_end: end to end
684 label_end_to_end: end to end
684 label_start_to_start: start to start
685 label_start_to_start: start to start
685 label_start_to_end: start to end
686 label_start_to_end: start to end
686 label_stay_logged_in: Stay logged in
687 label_stay_logged_in: Stay logged in
687 label_disabled: disabled
688 label_disabled: disabled
688 label_show_completed_versions: Show completed versions
689 label_show_completed_versions: Show completed versions
689 label_me: me
690 label_me: me
690 label_board: Forum
691 label_board: Forum
691 label_board_new: New forum
692 label_board_new: New forum
692 label_board_plural: Forums
693 label_board_plural: Forums
693 label_board_locked: Locked
694 label_board_locked: Locked
694 label_board_sticky: Sticky
695 label_board_sticky: Sticky
695 label_topic_plural: Topics
696 label_topic_plural: Topics
696 label_message_plural: Messages
697 label_message_plural: Messages
697 label_message_last: Last message
698 label_message_last: Last message
698 label_message_new: New message
699 label_message_new: New message
699 label_message_posted: Message added
700 label_message_posted: Message added
700 label_reply_plural: Replies
701 label_reply_plural: Replies
701 label_send_information: Send account information to the user
702 label_send_information: Send account information to the user
702 label_year: Year
703 label_year: Year
703 label_month: Month
704 label_month: Month
704 label_week: Week
705 label_week: Week
705 label_date_from: From
706 label_date_from: From
706 label_date_to: To
707 label_date_to: To
707 label_language_based: Based on user's language
708 label_language_based: Based on user's language
708 label_sort_by: "Sort by {{value}}"
709 label_sort_by: "Sort by {{value}}"
709 label_send_test_email: Send a test email
710 label_send_test_email: Send a test email
710 label_feeds_access_key: RSS access key
711 label_feeds_access_key: RSS access key
711 label_missing_feeds_access_key: Missing a RSS access key
712 label_missing_feeds_access_key: Missing a RSS access key
712 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
713 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
713 label_module_plural: Modules
714 label_module_plural: Modules
714 label_added_time_by: "Added by {{author}} {{age}} ago"
715 label_added_time_by: "Added by {{author}} {{age}} ago"
715 label_updated_time_by: "Updated by {{author}} {{age}} ago"
716 label_updated_time_by: "Updated by {{author}} {{age}} ago"
716 label_updated_time: "Updated {{value}} ago"
717 label_updated_time: "Updated {{value}} ago"
717 label_jump_to_a_project: Jump to a project...
718 label_jump_to_a_project: Jump to a project...
718 label_file_plural: Files
719 label_file_plural: Files
719 label_changeset_plural: Changesets
720 label_changeset_plural: Changesets
720 label_default_columns: Default columns
721 label_default_columns: Default columns
721 label_no_change_option: (No change)
722 label_no_change_option: (No change)
722 label_bulk_edit_selected_issues: Bulk edit selected issues
723 label_bulk_edit_selected_issues: Bulk edit selected issues
723 label_theme: Theme
724 label_theme: Theme
724 label_default: Default
725 label_default: Default
725 label_search_titles_only: Search titles only
726 label_search_titles_only: Search titles only
726 label_user_mail_option_all: "For any event on all my projects"
727 label_user_mail_option_all: "For any event on all my projects"
727 label_user_mail_option_selected: "For any event on the selected projects only..."
728 label_user_mail_option_selected: "For any event on the selected projects only..."
728 label_user_mail_option_none: "No events"
729 label_user_mail_option_none: "No events"
729 label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
730 label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
730 label_user_mail_option_only_assigned: "Only for things I am assigned to"
731 label_user_mail_option_only_assigned: "Only for things I am assigned to"
731 label_user_mail_option_only_owner: "Only for things I am the owner of"
732 label_user_mail_option_only_owner: "Only for things I am the owner of"
732 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
733 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
733 label_registration_activation_by_email: account activation by email
734 label_registration_activation_by_email: account activation by email
734 label_registration_manual_activation: manual account activation
735 label_registration_manual_activation: manual account activation
735 label_registration_automatic_activation: automatic account activation
736 label_registration_automatic_activation: automatic account activation
736 label_display_per_page: "Per page: {{value}}"
737 label_display_per_page: "Per page: {{value}}"
737 label_age: Age
738 label_age: Age
738 label_change_properties: Change properties
739 label_change_properties: Change properties
739 label_general: General
740 label_general: General
740 label_more: More
741 label_more: More
741 label_scm: SCM
742 label_scm: SCM
742 label_plugins: Plugins
743 label_plugins: Plugins
743 label_ldap_authentication: LDAP authentication
744 label_ldap_authentication: LDAP authentication
744 label_downloads_abbr: D/L
745 label_downloads_abbr: D/L
745 label_optional_description: Optional description
746 label_optional_description: Optional description
746 label_add_another_file: Add another file
747 label_add_another_file: Add another file
747 label_preferences: Preferences
748 label_preferences: Preferences
748 label_chronological_order: In chronological order
749 label_chronological_order: In chronological order
749 label_reverse_chronological_order: In reverse chronological order
750 label_reverse_chronological_order: In reverse chronological order
750 label_planning: Planning
751 label_planning: Planning
751 label_incoming_emails: Incoming emails
752 label_incoming_emails: Incoming emails
752 label_generate_key: Generate a key
753 label_generate_key: Generate a key
753 label_issue_watchers: Watchers
754 label_issue_watchers: Watchers
754 label_example: Example
755 label_example: Example
755 label_display: Display
756 label_display: Display
756 label_sort: Sort
757 label_sort: Sort
757 label_ascending: Ascending
758 label_ascending: Ascending
758 label_descending: Descending
759 label_descending: Descending
759 label_date_from_to: From {{start}} to {{end}}
760 label_date_from_to: From {{start}} to {{end}}
760 label_wiki_content_added: Wiki page added
761 label_wiki_content_added: Wiki page added
761 label_wiki_content_updated: Wiki page updated
762 label_wiki_content_updated: Wiki page updated
762 label_group: Group
763 label_group: Group
763 label_group_plural: Groups
764 label_group_plural: Groups
764 label_group_new: New group
765 label_group_new: New group
765 label_time_entry_plural: Spent time
766 label_time_entry_plural: Spent time
766 label_version_sharing_none: Not shared
767 label_version_sharing_none: Not shared
767 label_version_sharing_descendants: With subprojects
768 label_version_sharing_descendants: With subprojects
768 label_version_sharing_hierarchy: With project hierarchy
769 label_version_sharing_hierarchy: With project hierarchy
769 label_version_sharing_tree: With project tree
770 label_version_sharing_tree: With project tree
770 label_version_sharing_system: With all projects
771 label_version_sharing_system: With all projects
771 label_update_issue_done_ratios: Update issue done ratios
772 label_update_issue_done_ratios: Update issue done ratios
772 label_copy_source: Source
773 label_copy_source: Source
773 label_copy_target: Target
774 label_copy_target: Target
774 label_copy_same_as_target: Same as target
775 label_copy_same_as_target: Same as target
775 label_display_used_statuses_only: Only display statuses that are used by this tracker
776 label_display_used_statuses_only: Only display statuses that are used by this tracker
776 label_api_access_key: API access key
777 label_api_access_key: API access key
777 label_missing_api_access_key: Missing an API access key
778 label_missing_api_access_key: Missing an API access key
778 label_api_access_key_created_on: "API access key created {{value}} ago"
779 label_api_access_key_created_on: "API access key created {{value}} ago"
779 label_profile: Profile
780 label_profile: Profile
780 label_subtask_plural: Subtasks
781 label_subtask_plural: Subtasks
781 label_project_copy_notifications: Send email notifications during the project copy
782 label_project_copy_notifications: Send email notifications during the project copy
782
783
783 button_login: Login
784 button_login: Login
784 button_submit: Submit
785 button_submit: Submit
785 button_save: Save
786 button_save: Save
786 button_check_all: Check all
787 button_check_all: Check all
787 button_uncheck_all: Uncheck all
788 button_uncheck_all: Uncheck all
788 button_delete: Delete
789 button_delete: Delete
789 button_create: Create
790 button_create: Create
790 button_create_and_continue: Create and continue
791 button_create_and_continue: Create and continue
791 button_test: Test
792 button_test: Test
792 button_edit: Edit
793 button_edit: Edit
793 button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
794 button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
794 button_add: Add
795 button_add: Add
795 button_change: Change
796 button_change: Change
796 button_apply: Apply
797 button_apply: Apply
797 button_clear: Clear
798 button_clear: Clear
798 button_lock: Lock
799 button_lock: Lock
799 button_unlock: Unlock
800 button_unlock: Unlock
800 button_download: Download
801 button_download: Download
801 button_list: List
802 button_list: List
802 button_view: View
803 button_view: View
803 button_move: Move
804 button_move: Move
804 button_move_and_follow: Move and follow
805 button_move_and_follow: Move and follow
805 button_back: Back
806 button_back: Back
806 button_cancel: Cancel
807 button_cancel: Cancel
807 button_activate: Activate
808 button_activate: Activate
808 button_sort: Sort
809 button_sort: Sort
809 button_log_time: Log time
810 button_log_time: Log time
810 button_rollback: Rollback to this version
811 button_rollback: Rollback to this version
811 button_watch: Watch
812 button_watch: Watch
812 button_unwatch: Unwatch
813 button_unwatch: Unwatch
813 button_reply: Reply
814 button_reply: Reply
814 button_archive: Archive
815 button_archive: Archive
815 button_unarchive: Unarchive
816 button_unarchive: Unarchive
816 button_reset: Reset
817 button_reset: Reset
817 button_rename: Rename
818 button_rename: Rename
818 button_change_password: Change password
819 button_change_password: Change password
819 button_copy: Copy
820 button_copy: Copy
820 button_copy_and_follow: Copy and follow
821 button_copy_and_follow: Copy and follow
821 button_annotate: Annotate
822 button_annotate: Annotate
822 button_update: Update
823 button_update: Update
823 button_configure: Configure
824 button_configure: Configure
824 button_quote: Quote
825 button_quote: Quote
825 button_duplicate: Duplicate
826 button_duplicate: Duplicate
826 button_show: Show
827 button_show: Show
827
828
828 status_active: active
829 status_active: active
829 status_registered: registered
830 status_registered: registered
830 status_locked: locked
831 status_locked: locked
831
832
832 version_status_open: open
833 version_status_open: open
833 version_status_locked: locked
834 version_status_locked: locked
834 version_status_closed: closed
835 version_status_closed: closed
835
836
836 field_active: Active
837 field_active: Active
837
838
838 text_select_mail_notifications: Select actions for which email notifications should be sent.
839 text_select_mail_notifications: Select actions for which email notifications should be sent.
839 text_regexp_info: eg. ^[A-Z0-9]+$
840 text_regexp_info: eg. ^[A-Z0-9]+$
840 text_min_max_length_info: 0 means no restriction
841 text_min_max_length_info: 0 means no restriction
841 text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
842 text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
842 text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
843 text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
843 text_workflow_edit: Select a role and a tracker to edit the workflow
844 text_workflow_edit: Select a role and a tracker to edit the workflow
844 text_are_you_sure: Are you sure ?
845 text_are_you_sure: Are you sure ?
845 text_are_you_sure_with_children: "Delete issue and all child issues?"
846 text_are_you_sure_with_children: "Delete issue and all child issues?"
846 text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
847 text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
847 text_journal_set_to: "{{label}} set to {{value}}"
848 text_journal_set_to: "{{label}} set to {{value}}"
848 text_journal_deleted: "{{label}} deleted ({{old}})"
849 text_journal_deleted: "{{label}} deleted ({{old}})"
849 text_journal_added: "{{label}} {{value}} added"
850 text_journal_added: "{{label}} {{value}} added"
850 text_tip_task_begin_day: task beginning this day
851 text_tip_task_begin_day: task beginning this day
851 text_tip_task_end_day: task ending this day
852 text_tip_task_end_day: task ending this day
852 text_tip_task_begin_end_day: task beginning and ending this day
853 text_tip_task_begin_end_day: task beginning and ending this day
853 text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br />Once saved, the identifier can not be changed.'
854 text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br />Once saved, the identifier can not be changed.'
854 text_caracters_maximum: "{{count}} characters maximum."
855 text_caracters_maximum: "{{count}} characters maximum."
855 text_caracters_minimum: "Must be at least {{count}} characters long."
856 text_caracters_minimum: "Must be at least {{count}} characters long."
856 text_length_between: "Length between {{min}} and {{max}} characters."
857 text_length_between: "Length between {{min}} and {{max}} characters."
857 text_tracker_no_workflow: No workflow defined for this tracker
858 text_tracker_no_workflow: No workflow defined for this tracker
858 text_unallowed_characters: Unallowed characters
859 text_unallowed_characters: Unallowed characters
859 text_comma_separated: Multiple values allowed (comma separated).
860 text_comma_separated: Multiple values allowed (comma separated).
860 text_line_separated: Multiple values allowed (one line for each value).
861 text_line_separated: Multiple values allowed (one line for each value).
861 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
862 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
862 text_issue_added: "Issue {{id}} has been reported by {{author}}."
863 text_issue_added: "Issue {{id}} has been reported by {{author}}."
863 text_issue_updated: "Issue {{id}} has been updated by {{author}}."
864 text_issue_updated: "Issue {{id}} has been updated by {{author}}."
864 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
865 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
865 text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
866 text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
866 text_issue_category_destroy_assignments: Remove category assignments
867 text_issue_category_destroy_assignments: Remove category assignments
867 text_issue_category_reassign_to: Reassign issues to this category
868 text_issue_category_reassign_to: Reassign issues to this category
868 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)."
869 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)."
869 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."
870 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."
870 text_load_default_configuration: Load the default configuration
871 text_load_default_configuration: Load the default configuration
871 text_status_changed_by_changeset: "Applied in changeset {{value}}."
872 text_status_changed_by_changeset: "Applied in changeset {{value}}."
872 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
873 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
873 text_select_project_modules: 'Select modules to enable for this project:'
874 text_select_project_modules: 'Select modules to enable for this project:'
874 text_default_administrator_account_changed: Default administrator account changed
875 text_default_administrator_account_changed: Default administrator account changed
875 text_file_repository_writable: Attachments directory writable
876 text_file_repository_writable: Attachments directory writable
876 text_plugin_assets_writable: Plugin assets directory writable
877 text_plugin_assets_writable: Plugin assets directory writable
877 text_rmagick_available: RMagick available (optional)
878 text_rmagick_available: RMagick available (optional)
878 text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
879 text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
879 text_destroy_time_entries: Delete reported hours
880 text_destroy_time_entries: Delete reported hours
880 text_assign_time_entries_to_project: Assign reported hours to the project
881 text_assign_time_entries_to_project: Assign reported hours to the project
881 text_reassign_time_entries: 'Reassign reported hours to this issue:'
882 text_reassign_time_entries: 'Reassign reported hours to this issue:'
882 text_user_wrote: "{{value}} wrote:"
883 text_user_wrote: "{{value}} wrote:"
883 text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
884 text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
884 text_enumeration_category_reassign_to: 'Reassign them to this value:'
885 text_enumeration_category_reassign_to: 'Reassign them to this value:'
885 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."
886 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."
886 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."
887 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."
887 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
888 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
888 text_custom_field_possible_values_info: 'One line for each value'
889 text_custom_field_possible_values_info: 'One line for each value'
889 text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
890 text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
890 text_wiki_page_nullify_children: "Keep child pages as root pages"
891 text_wiki_page_nullify_children: "Keep child pages as root pages"
891 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
892 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
892 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
893 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
893 text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
894 text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
894 text_zoom_in: Zoom in
895 text_zoom_in: Zoom in
895 text_zoom_out: Zoom out
896 text_zoom_out: Zoom out
896
897
897 default_role_manager: Manager
898 default_role_manager: Manager
898 default_role_developer: Developer
899 default_role_developer: Developer
899 default_role_reporter: Reporter
900 default_role_reporter: Reporter
900 default_tracker_bug: Bug
901 default_tracker_bug: Bug
901 default_tracker_feature: Feature
902 default_tracker_feature: Feature
902 default_tracker_support: Support
903 default_tracker_support: Support
903 default_issue_status_new: New
904 default_issue_status_new: New
904 default_issue_status_in_progress: In Progress
905 default_issue_status_in_progress: In Progress
905 default_issue_status_resolved: Resolved
906 default_issue_status_resolved: Resolved
906 default_issue_status_feedback: Feedback
907 default_issue_status_feedback: Feedback
907 default_issue_status_closed: Closed
908 default_issue_status_closed: Closed
908 default_issue_status_rejected: Rejected
909 default_issue_status_rejected: Rejected
909 default_doc_category_user: User documentation
910 default_doc_category_user: User documentation
910 default_doc_category_tech: Technical documentation
911 default_doc_category_tech: Technical documentation
911 default_priority_low: Low
912 default_priority_low: Low
912 default_priority_normal: Normal
913 default_priority_normal: Normal
913 default_priority_high: High
914 default_priority_high: High
914 default_priority_urgent: Urgent
915 default_priority_urgent: Urgent
915 default_priority_immediate: Immediate
916 default_priority_immediate: Immediate
916 default_activity_design: Design
917 default_activity_design: Design
917 default_activity_development: Development
918 default_activity_development: Development
918
919
919 enumeration_issue_priorities: Issue priorities
920 enumeration_issue_priorities: Issue priorities
920 enumeration_doc_categories: Document categories
921 enumeration_doc_categories: Document categories
921 enumeration_activities: Activities (time tracking)
922 enumeration_activities: Activities (time tracking)
922 enumeration_system_activity: System Activity
923 enumeration_system_activity: System Activity
923
924
@@ -1,186 +1,188
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 cache_formatted_text:
56 cache_formatted_text:
57 default: 0
57 default: 0
58 wiki_compression:
58 wiki_compression:
59 default: ""
59 default: ""
60 default_language:
60 default_language:
61 default: en
61 default: en
62 host_name:
62 host_name:
63 default: localhost:3000
63 default: localhost:3000
64 protocol:
64 protocol:
65 default: http
65 default: http
66 feeds_limit:
66 feeds_limit:
67 format: int
67 format: int
68 default: 15
68 default: 15
69 # Maximum size of files that can be displayed
69 # Maximum size of files that can be displayed
70 # inline through the file viewer (in KB)
70 # inline through the file viewer (in KB)
71 file_max_size_displayed:
71 file_max_size_displayed:
72 format: int
72 format: int
73 default: 512
73 default: 512
74 diff_max_lines_displayed:
74 diff_max_lines_displayed:
75 format: int
75 format: int
76 default: 1500
76 default: 1500
77 enabled_scm:
77 enabled_scm:
78 serialized: true
78 serialized: true
79 default:
79 default:
80 - Subversion
80 - Subversion
81 - Darcs
81 - Darcs
82 - Mercurial
82 - Mercurial
83 - Cvs
83 - Cvs
84 - Bazaar
84 - Bazaar
85 - Git
85 - Git
86 autofetch_changesets:
86 autofetch_changesets:
87 default: 1
87 default: 1
88 sys_api_enabled:
88 sys_api_enabled:
89 default: 0
89 default: 0
90 sys_api_key:
90 sys_api_key:
91 default: ''
91 default: ''
92 commit_ref_keywords:
92 commit_ref_keywords:
93 default: 'refs,references,IssueID'
93 default: 'refs,references,IssueID'
94 commit_fix_keywords:
94 commit_fix_keywords:
95 default: 'fixes,closes'
95 default: 'fixes,closes'
96 commit_fix_status_id:
96 commit_fix_status_id:
97 format: int
97 format: int
98 default: 0
98 default: 0
99 commit_fix_done_ratio:
99 commit_fix_done_ratio:
100 default: 100
100 default: 100
101 # autologin duration in days
101 # autologin duration in days
102 # 0 means autologin is disabled
102 # 0 means autologin is disabled
103 autologin:
103 autologin:
104 format: int
104 format: int
105 default: 0
105 default: 0
106 # date format
106 # date format
107 date_format:
107 date_format:
108 default: ''
108 default: ''
109 time_format:
109 time_format:
110 default: ''
110 default: ''
111 user_format:
111 user_format:
112 default: :firstname_lastname
112 default: :firstname_lastname
113 format: symbol
113 format: symbol
114 cross_project_issue_relations:
114 cross_project_issue_relations:
115 default: 0
115 default: 0
116 notified_events:
116 notified_events:
117 serialized: true
117 serialized: true
118 default:
118 default:
119 - issue_added
119 - issue_added
120 - issue_updated
120 - issue_updated
121 mail_handler_body_delimiters:
121 mail_handler_body_delimiters:
122 default: ''
122 default: ''
123 mail_handler_api_enabled:
123 mail_handler_api_enabled:
124 default: 0
124 default: 0
125 mail_handler_api_key:
125 mail_handler_api_key:
126 default:
126 default:
127 issue_list_default_columns:
127 issue_list_default_columns:
128 serialized: true
128 serialized: true
129 default:
129 default:
130 - tracker
130 - tracker
131 - status
131 - status
132 - priority
132 - priority
133 - subject
133 - subject
134 - assigned_to
134 - assigned_to
135 - updated_on
135 - updated_on
136 display_subprojects_issues:
136 display_subprojects_issues:
137 default: 1
137 default: 1
138 issue_done_ratio:
138 issue_done_ratio:
139 default: 'issue_field'
139 default: 'issue_field'
140 default_projects_public:
140 default_projects_public:
141 default: 1
141 default: 1
142 default_projects_modules:
142 default_projects_modules:
143 serialized: true
143 serialized: true
144 default:
144 default:
145 - issue_tracking
145 - issue_tracking
146 - time_tracking
146 - time_tracking
147 - news
147 - news
148 - documents
148 - documents
149 - files
149 - files
150 - wiki
150 - wiki
151 - repository
151 - repository
152 - boards
152 - boards
153 - calendar
153 - calendar
154 - gantt
154 - gantt
155 # Role given to a non-admin user who creates a project
155 # Role given to a non-admin user who creates a project
156 new_project_user_role_id:
156 new_project_user_role_id:
157 format: int
157 format: int
158 default: ''
158 default: ''
159 sequential_project_identifiers:
159 sequential_project_identifiers:
160 default: 0
160 default: 0
161 # encodings used to convert repository files content to UTF-8
161 # encodings used to convert repository files content to UTF-8
162 # multiple values accepted, comma separated
162 # multiple values accepted, comma separated
163 repositories_encodings:
163 repositories_encodings:
164 default: ''
164 default: ''
165 # encoding used to convert commit logs to UTF-8
165 # encoding used to convert commit logs to UTF-8
166 commit_logs_encoding:
166 commit_logs_encoding:
167 default: 'UTF-8'
167 default: 'UTF-8'
168 repository_log_display_limit:
168 repository_log_display_limit:
169 format: int
169 format: int
170 default: 100
170 default: 100
171 ui_theme:
171 ui_theme:
172 default: ''
172 default: ''
173 emails_footer:
173 emails_footer:
174 default: |-
174 default: |-
175 You have received this notification because you have either subscribed to it, or are involved in it.
175 You have received this notification because you have either subscribed to it, or are involved in it.
176 To change your notification preferences, please click here: http://hostname/my/account
176 To change your notification preferences, please click here: http://hostname/my/account
177 gravatar_enabled:
177 gravatar_enabled:
178 default: 0
178 default: 0
179 openid:
179 openid:
180 default: 0
180 default: 0
181 gravatar_default:
181 gravatar_default:
182 default: ''
182 default: ''
183 start_of_week:
183 start_of_week:
184 default: ''
184 default: ''
185 rest_api_enabled:
185 rest_api_enabled:
186 default: 0
186 default: 0
187 default_notification_option:
188 default: 'only_my_events'
@@ -1,502 +1,514
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 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources
22
22
23 def setup
23 def setup
24 @admin = User.find(1)
24 @admin = User.find(1)
25 @jsmith = User.find(2)
25 @jsmith = User.find(2)
26 @dlopper = User.find(3)
26 @dlopper = User.find(3)
27 end
27 end
28
28
29 test 'object_daddy creation' do
29 test 'object_daddy creation' do
30 User.generate_with_protected!(:firstname => 'Testing connection')
30 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
33 end
33 end
34
34
35 def test_truth
35 def test_truth
36 assert_kind_of User, @jsmith
36 assert_kind_of User, @jsmith
37 end
37 end
38
38
39 def test_mail_should_be_stripped
39 def test_mail_should_be_stripped
40 u = User.new
40 u = User.new
41 u.mail = " foo@bar.com "
41 u.mail = " foo@bar.com "
42 assert_equal "foo@bar.com", u.mail
42 assert_equal "foo@bar.com", u.mail
43 end
43 end
44
44
45 def test_create
45 def test_create
46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
47
47
48 user.login = "jsmith"
48 user.login = "jsmith"
49 user.password, user.password_confirmation = "password", "password"
49 user.password, user.password_confirmation = "password", "password"
50 # login uniqueness
50 # login uniqueness
51 assert !user.save
51 assert !user.save
52 assert_equal 1, user.errors.count
52 assert_equal 1, user.errors.count
53
53
54 user.login = "newuser"
54 user.login = "newuser"
55 user.password, user.password_confirmation = "passwd", "password"
55 user.password, user.password_confirmation = "passwd", "password"
56 # password confirmation
56 # password confirmation
57 assert !user.save
57 assert !user.save
58 assert_equal 1, user.errors.count
58 assert_equal 1, user.errors.count
59
59
60 user.password, user.password_confirmation = "password", "password"
60 user.password, user.password_confirmation = "password", "password"
61 assert user.save
61 assert user.save
62 end
62 end
63
63
64 context "User#before_create" do
65 should "set the mail_notification to the default Setting" do
66 @user1 = User.generate_with_protected!
67 assert_equal 'only_my_events', @user1.mail_notification
68
69 with_settings :default_notification_option => 'all' do
70 @user2 = User.generate_with_protected!
71 assert_equal 'all', @user2.mail_notification
72 end
73 end
74 end
75
64 context "User.login" do
76 context "User.login" do
65 should "be case-insensitive." do
77 should "be case-insensitive." do
66 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
78 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
67 u.login = 'newuser'
79 u.login = 'newuser'
68 u.password, u.password_confirmation = "password", "password"
80 u.password, u.password_confirmation = "password", "password"
69 assert u.save
81 assert u.save
70
82
71 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
83 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
72 u.login = 'NewUser'
84 u.login = 'NewUser'
73 u.password, u.password_confirmation = "password", "password"
85 u.password, u.password_confirmation = "password", "password"
74 assert !u.save
86 assert !u.save
75 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
87 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
76 end
88 end
77 end
89 end
78
90
79 def test_mail_uniqueness_should_not_be_case_sensitive
91 def test_mail_uniqueness_should_not_be_case_sensitive
80 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
92 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
81 u.login = 'newuser1'
93 u.login = 'newuser1'
82 u.password, u.password_confirmation = "password", "password"
94 u.password, u.password_confirmation = "password", "password"
83 assert u.save
95 assert u.save
84
96
85 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
97 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
86 u.login = 'newuser2'
98 u.login = 'newuser2'
87 u.password, u.password_confirmation = "password", "password"
99 u.password, u.password_confirmation = "password", "password"
88 assert !u.save
100 assert !u.save
89 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
101 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
90 end
102 end
91
103
92 def test_update
104 def test_update
93 assert_equal "admin", @admin.login
105 assert_equal "admin", @admin.login
94 @admin.login = "john"
106 @admin.login = "john"
95 assert @admin.save, @admin.errors.full_messages.join("; ")
107 assert @admin.save, @admin.errors.full_messages.join("; ")
96 @admin.reload
108 @admin.reload
97 assert_equal "john", @admin.login
109 assert_equal "john", @admin.login
98 end
110 end
99
111
100 def test_destroy
112 def test_destroy
101 User.find(2).destroy
113 User.find(2).destroy
102 assert_nil User.find_by_id(2)
114 assert_nil User.find_by_id(2)
103 assert Member.find_all_by_user_id(2).empty?
115 assert Member.find_all_by_user_id(2).empty?
104 end
116 end
105
117
106 def test_validate
118 def test_validate
107 @admin.login = ""
119 @admin.login = ""
108 assert !@admin.save
120 assert !@admin.save
109 assert_equal 1, @admin.errors.count
121 assert_equal 1, @admin.errors.count
110 end
122 end
111
123
112 context "User#try_to_login" do
124 context "User#try_to_login" do
113 should "fall-back to case-insensitive if user login is not found as-typed." do
125 should "fall-back to case-insensitive if user login is not found as-typed." do
114 user = User.try_to_login("AdMin", "admin")
126 user = User.try_to_login("AdMin", "admin")
115 assert_kind_of User, user
127 assert_kind_of User, user
116 assert_equal "admin", user.login
128 assert_equal "admin", user.login
117 end
129 end
118
130
119 should "select the exact matching user first" do
131 should "select the exact matching user first" do
120 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
132 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
121 # bypass validations to make it appear like existing data
133 # bypass validations to make it appear like existing data
122 case_sensitive_user.update_attribute(:login, 'ADMIN')
134 case_sensitive_user.update_attribute(:login, 'ADMIN')
123
135
124 user = User.try_to_login("ADMIN", "admin")
136 user = User.try_to_login("ADMIN", "admin")
125 assert_kind_of User, user
137 assert_kind_of User, user
126 assert_equal "ADMIN", user.login
138 assert_equal "ADMIN", user.login
127
139
128 end
140 end
129 end
141 end
130
142
131 def test_password
143 def test_password
132 user = User.try_to_login("admin", "admin")
144 user = User.try_to_login("admin", "admin")
133 assert_kind_of User, user
145 assert_kind_of User, user
134 assert_equal "admin", user.login
146 assert_equal "admin", user.login
135 user.password = "hello"
147 user.password = "hello"
136 assert user.save
148 assert user.save
137
149
138 user = User.try_to_login("admin", "hello")
150 user = User.try_to_login("admin", "hello")
139 assert_kind_of User, user
151 assert_kind_of User, user
140 assert_equal "admin", user.login
152 assert_equal "admin", user.login
141 assert_equal User.hash_password("hello"), user.hashed_password
153 assert_equal User.hash_password("hello"), user.hashed_password
142 end
154 end
143
155
144 def test_name_format
156 def test_name_format
145 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
157 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
146 Setting.user_format = :firstname_lastname
158 Setting.user_format = :firstname_lastname
147 assert_equal 'John Smith', @jsmith.reload.name
159 assert_equal 'John Smith', @jsmith.reload.name
148 Setting.user_format = :username
160 Setting.user_format = :username
149 assert_equal 'jsmith', @jsmith.reload.name
161 assert_equal 'jsmith', @jsmith.reload.name
150 end
162 end
151
163
152 def test_lock
164 def test_lock
153 user = User.try_to_login("jsmith", "jsmith")
165 user = User.try_to_login("jsmith", "jsmith")
154 assert_equal @jsmith, user
166 assert_equal @jsmith, user
155
167
156 @jsmith.status = User::STATUS_LOCKED
168 @jsmith.status = User::STATUS_LOCKED
157 assert @jsmith.save
169 assert @jsmith.save
158
170
159 user = User.try_to_login("jsmith", "jsmith")
171 user = User.try_to_login("jsmith", "jsmith")
160 assert_equal nil, user
172 assert_equal nil, user
161 end
173 end
162
174
163 if ldap_configured?
175 if ldap_configured?
164 context "#try_to_login using LDAP" do
176 context "#try_to_login using LDAP" do
165 context "with failed connection to the LDAP server" do
177 context "with failed connection to the LDAP server" do
166 should "return nil" do
178 should "return nil" do
167 @auth_source = AuthSourceLdap.find(1)
179 @auth_source = AuthSourceLdap.find(1)
168 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
180 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
169
181
170 assert_equal nil, User.try_to_login('edavis', 'wrong')
182 assert_equal nil, User.try_to_login('edavis', 'wrong')
171 end
183 end
172 end
184 end
173
185
174 context "with an unsuccessful authentication" do
186 context "with an unsuccessful authentication" do
175 should "return nil" do
187 should "return nil" do
176 assert_equal nil, User.try_to_login('edavis', 'wrong')
188 assert_equal nil, User.try_to_login('edavis', 'wrong')
177 end
189 end
178 end
190 end
179
191
180 context "on the fly registration" do
192 context "on the fly registration" do
181 setup do
193 setup do
182 @auth_source = AuthSourceLdap.find(1)
194 @auth_source = AuthSourceLdap.find(1)
183 end
195 end
184
196
185 context "with a successful authentication" do
197 context "with a successful authentication" do
186 should "create a new user account if it doesn't exist" do
198 should "create a new user account if it doesn't exist" do
187 assert_difference('User.count') do
199 assert_difference('User.count') do
188 user = User.try_to_login('edavis', '123456')
200 user = User.try_to_login('edavis', '123456')
189 assert !user.admin?
201 assert !user.admin?
190 end
202 end
191 end
203 end
192
204
193 should "retrieve existing user" do
205 should "retrieve existing user" do
194 user = User.try_to_login('edavis', '123456')
206 user = User.try_to_login('edavis', '123456')
195 user.admin = true
207 user.admin = true
196 user.save!
208 user.save!
197
209
198 assert_no_difference('User.count') do
210 assert_no_difference('User.count') do
199 user = User.try_to_login('edavis', '123456')
211 user = User.try_to_login('edavis', '123456')
200 assert user.admin?
212 assert user.admin?
201 end
213 end
202 end
214 end
203 end
215 end
204 end
216 end
205 end
217 end
206
218
207 else
219 else
208 puts "Skipping LDAP tests."
220 puts "Skipping LDAP tests."
209 end
221 end
210
222
211 def test_create_anonymous
223 def test_create_anonymous
212 AnonymousUser.delete_all
224 AnonymousUser.delete_all
213 anon = User.anonymous
225 anon = User.anonymous
214 assert !anon.new_record?
226 assert !anon.new_record?
215 assert_kind_of AnonymousUser, anon
227 assert_kind_of AnonymousUser, anon
216 end
228 end
217
229
218 should_have_one :rss_token
230 should_have_one :rss_token
219
231
220 def test_rss_key
232 def test_rss_key
221 assert_nil @jsmith.rss_token
233 assert_nil @jsmith.rss_token
222 key = @jsmith.rss_key
234 key = @jsmith.rss_key
223 assert_equal 40, key.length
235 assert_equal 40, key.length
224
236
225 @jsmith.reload
237 @jsmith.reload
226 assert_equal key, @jsmith.rss_key
238 assert_equal key, @jsmith.rss_key
227 end
239 end
228
240
229
241
230 should_have_one :api_token
242 should_have_one :api_token
231
243
232 context "User#api_key" do
244 context "User#api_key" do
233 should "generate a new one if the user doesn't have one" do
245 should "generate a new one if the user doesn't have one" do
234 user = User.generate_with_protected!(:api_token => nil)
246 user = User.generate_with_protected!(:api_token => nil)
235 assert_nil user.api_token
247 assert_nil user.api_token
236
248
237 key = user.api_key
249 key = user.api_key
238 assert_equal 40, key.length
250 assert_equal 40, key.length
239 user.reload
251 user.reload
240 assert_equal key, user.api_key
252 assert_equal key, user.api_key
241 end
253 end
242
254
243 should "return the existing api token value" do
255 should "return the existing api token value" do
244 user = User.generate_with_protected!
256 user = User.generate_with_protected!
245 token = Token.generate!(:action => 'api')
257 token = Token.generate!(:action => 'api')
246 user.api_token = token
258 user.api_token = token
247 assert user.save
259 assert user.save
248
260
249 assert_equal token.value, user.api_key
261 assert_equal token.value, user.api_key
250 end
262 end
251 end
263 end
252
264
253 context "User#find_by_api_key" do
265 context "User#find_by_api_key" do
254 should "return nil if no matching key is found" do
266 should "return nil if no matching key is found" do
255 assert_nil User.find_by_api_key('zzzzzzzzz')
267 assert_nil User.find_by_api_key('zzzzzzzzz')
256 end
268 end
257
269
258 should "return nil if the key is found for an inactive user" do
270 should "return nil if the key is found for an inactive user" do
259 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
271 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
260 token = Token.generate!(:action => 'api')
272 token = Token.generate!(:action => 'api')
261 user.api_token = token
273 user.api_token = token
262 user.save
274 user.save
263
275
264 assert_nil User.find_by_api_key(token.value)
276 assert_nil User.find_by_api_key(token.value)
265 end
277 end
266
278
267 should "return the user if the key is found for an active user" do
279 should "return the user if the key is found for an active user" do
268 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
280 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
269 token = Token.generate!(:action => 'api')
281 token = Token.generate!(:action => 'api')
270 user.api_token = token
282 user.api_token = token
271 user.save
283 user.save
272
284
273 assert_equal user, User.find_by_api_key(token.value)
285 assert_equal user, User.find_by_api_key(token.value)
274 end
286 end
275 end
287 end
276
288
277 def test_roles_for_project
289 def test_roles_for_project
278 # user with a role
290 # user with a role
279 roles = @jsmith.roles_for_project(Project.find(1))
291 roles = @jsmith.roles_for_project(Project.find(1))
280 assert_kind_of Role, roles.first
292 assert_kind_of Role, roles.first
281 assert_equal "Manager", roles.first.name
293 assert_equal "Manager", roles.first.name
282
294
283 # user with no role
295 # user with no role
284 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
296 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
285 end
297 end
286
298
287 def test_mail_notification_all
299 def test_mail_notification_all
288 @jsmith.mail_notification = 'all'
300 @jsmith.mail_notification = 'all'
289 @jsmith.notified_project_ids = []
301 @jsmith.notified_project_ids = []
290 @jsmith.save
302 @jsmith.save
291 @jsmith.reload
303 @jsmith.reload
292 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
304 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
293 end
305 end
294
306
295 def test_mail_notification_selected
307 def test_mail_notification_selected
296 @jsmith.mail_notification = 'selected'
308 @jsmith.mail_notification = 'selected'
297 @jsmith.notified_project_ids = [1]
309 @jsmith.notified_project_ids = [1]
298 @jsmith.save
310 @jsmith.save
299 @jsmith.reload
311 @jsmith.reload
300 assert Project.find(1).recipients.include?(@jsmith.mail)
312 assert Project.find(1).recipients.include?(@jsmith.mail)
301 end
313 end
302
314
303 def test_mail_notification_only_my_events
315 def test_mail_notification_only_my_events
304 @jsmith.mail_notification = 'only_my_events'
316 @jsmith.mail_notification = 'only_my_events'
305 @jsmith.notified_project_ids = []
317 @jsmith.notified_project_ids = []
306 @jsmith.save
318 @jsmith.save
307 @jsmith.reload
319 @jsmith.reload
308 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
320 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
309 end
321 end
310
322
311 def test_comments_sorting_preference
323 def test_comments_sorting_preference
312 assert !@jsmith.wants_comments_in_reverse_order?
324 assert !@jsmith.wants_comments_in_reverse_order?
313 @jsmith.pref.comments_sorting = 'asc'
325 @jsmith.pref.comments_sorting = 'asc'
314 assert !@jsmith.wants_comments_in_reverse_order?
326 assert !@jsmith.wants_comments_in_reverse_order?
315 @jsmith.pref.comments_sorting = 'desc'
327 @jsmith.pref.comments_sorting = 'desc'
316 assert @jsmith.wants_comments_in_reverse_order?
328 assert @jsmith.wants_comments_in_reverse_order?
317 end
329 end
318
330
319 def test_find_by_mail_should_be_case_insensitive
331 def test_find_by_mail_should_be_case_insensitive
320 u = User.find_by_mail('JSmith@somenet.foo')
332 u = User.find_by_mail('JSmith@somenet.foo')
321 assert_not_nil u
333 assert_not_nil u
322 assert_equal 'jsmith@somenet.foo', u.mail
334 assert_equal 'jsmith@somenet.foo', u.mail
323 end
335 end
324
336
325 def test_random_password
337 def test_random_password
326 u = User.new
338 u = User.new
327 u.random_password
339 u.random_password
328 assert !u.password.blank?
340 assert !u.password.blank?
329 assert !u.password_confirmation.blank?
341 assert !u.password_confirmation.blank?
330 end
342 end
331
343
332 context "#change_password_allowed?" do
344 context "#change_password_allowed?" do
333 should "be allowed if no auth source is set" do
345 should "be allowed if no auth source is set" do
334 user = User.generate_with_protected!
346 user = User.generate_with_protected!
335 assert user.change_password_allowed?
347 assert user.change_password_allowed?
336 end
348 end
337
349
338 should "delegate to the auth source" do
350 should "delegate to the auth source" do
339 user = User.generate_with_protected!
351 user = User.generate_with_protected!
340
352
341 allowed_auth_source = AuthSource.generate!
353 allowed_auth_source = AuthSource.generate!
342 def allowed_auth_source.allow_password_changes?; true; end
354 def allowed_auth_source.allow_password_changes?; true; end
343
355
344 denied_auth_source = AuthSource.generate!
356 denied_auth_source = AuthSource.generate!
345 def denied_auth_source.allow_password_changes?; false; end
357 def denied_auth_source.allow_password_changes?; false; end
346
358
347 assert user.change_password_allowed?
359 assert user.change_password_allowed?
348
360
349 user.auth_source = allowed_auth_source
361 user.auth_source = allowed_auth_source
350 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
362 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
351
363
352 user.auth_source = denied_auth_source
364 user.auth_source = denied_auth_source
353 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
365 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
354 end
366 end
355
367
356 end
368 end
357
369
358 context "#allowed_to?" do
370 context "#allowed_to?" do
359 context "with a unique project" do
371 context "with a unique project" do
360 should "return false if project is archived" do
372 should "return false if project is archived" do
361 project = Project.find(1)
373 project = Project.find(1)
362 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
374 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
363 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
375 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
364 end
376 end
365
377
366 should "return false if related module is disabled" do
378 should "return false if related module is disabled" do
367 project = Project.find(1)
379 project = Project.find(1)
368 project.enabled_module_names = ["issue_tracking"]
380 project.enabled_module_names = ["issue_tracking"]
369 assert @admin.allowed_to?(:add_issues, project)
381 assert @admin.allowed_to?(:add_issues, project)
370 assert ! @admin.allowed_to?(:view_wiki_pages, project)
382 assert ! @admin.allowed_to?(:view_wiki_pages, project)
371 end
383 end
372
384
373 should "authorize nearly everything for admin users" do
385 should "authorize nearly everything for admin users" do
374 project = Project.find(1)
386 project = Project.find(1)
375 assert ! @admin.member_of?(project)
387 assert ! @admin.member_of?(project)
376 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
388 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
377 assert @admin.allowed_to?(p.to_sym, project)
389 assert @admin.allowed_to?(p.to_sym, project)
378 end
390 end
379 end
391 end
380
392
381 should "authorize normal users depending on their roles" do
393 should "authorize normal users depending on their roles" do
382 project = Project.find(1)
394 project = Project.find(1)
383 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
395 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
384 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
396 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
385 end
397 end
386 end
398 end
387
399
388 context "with options[:global]" do
400 context "with options[:global]" do
389 should "authorize if user has at least one role that has this permission" do
401 should "authorize if user has at least one role that has this permission" do
390 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
402 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
391 @anonymous = User.find(6)
403 @anonymous = User.find(6)
392 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
404 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
393 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
405 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
394 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
406 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
395 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
407 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
396 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
408 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
397 end
409 end
398 end
410 end
399 end
411 end
400
412
401 context "User#notify_about?" do
413 context "User#notify_about?" do
402 context "Issues" do
414 context "Issues" do
403 setup do
415 setup do
404 @project = Project.find(1)
416 @project = Project.find(1)
405 @author = User.generate_with_protected!
417 @author = User.generate_with_protected!
406 @assignee = User.generate_with_protected!
418 @assignee = User.generate_with_protected!
407 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
419 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
408 end
420 end
409
421
410 should "be true for a user with :all" do
422 should "be true for a user with :all" do
411 @author.update_attribute(:mail_notification, :all)
423 @author.update_attribute(:mail_notification, :all)
412 assert @author.notify_about?(@issue)
424 assert @author.notify_about?(@issue)
413 end
425 end
414
426
415 should "be false for a user with :none" do
427 should "be false for a user with :none" do
416 @author.update_attribute(:mail_notification, :none)
428 @author.update_attribute(:mail_notification, :none)
417 assert ! @author.notify_about?(@issue)
429 assert ! @author.notify_about?(@issue)
418 end
430 end
419
431
420 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
432 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
421 @user = User.generate_with_protected!(:mail_notification => :only_my_events)
433 @user = User.generate_with_protected!(:mail_notification => :only_my_events)
422 assert ! @user.notify_about?(@issue)
434 assert ! @user.notify_about?(@issue)
423 end
435 end
424
436
425 should "be true for a user with :only_my_events and is the author" do
437 should "be true for a user with :only_my_events and is the author" do
426 @author.update_attribute(:mail_notification, :only_my_events)
438 @author.update_attribute(:mail_notification, :only_my_events)
427 assert @author.notify_about?(@issue)
439 assert @author.notify_about?(@issue)
428 end
440 end
429
441
430 should "be true for a user with :only_my_events and is the assignee" do
442 should "be true for a user with :only_my_events and is the assignee" do
431 @assignee.update_attribute(:mail_notification, :only_my_events)
443 @assignee.update_attribute(:mail_notification, :only_my_events)
432 assert @assignee.notify_about?(@issue)
444 assert @assignee.notify_about?(@issue)
433 end
445 end
434
446
435 should "be true for a user with :only_assigned and is the assignee" do
447 should "be true for a user with :only_assigned and is the assignee" do
436 @assignee.update_attribute(:mail_notification, :only_assigned)
448 @assignee.update_attribute(:mail_notification, :only_assigned)
437 assert @assignee.notify_about?(@issue)
449 assert @assignee.notify_about?(@issue)
438 end
450 end
439
451
440 should "be false for a user with :only_assigned and is not the assignee" do
452 should "be false for a user with :only_assigned and is not the assignee" do
441 @author.update_attribute(:mail_notification, :only_assigned)
453 @author.update_attribute(:mail_notification, :only_assigned)
442 assert ! @author.notify_about?(@issue)
454 assert ! @author.notify_about?(@issue)
443 end
455 end
444
456
445 should "be true for a user with :only_owner and is the author" do
457 should "be true for a user with :only_owner and is the author" do
446 @author.update_attribute(:mail_notification, :only_owner)
458 @author.update_attribute(:mail_notification, :only_owner)
447 assert @author.notify_about?(@issue)
459 assert @author.notify_about?(@issue)
448 end
460 end
449
461
450 should "be false for a user with :only_owner and is not the author" do
462 should "be false for a user with :only_owner and is not the author" do
451 @assignee.update_attribute(:mail_notification, :only_owner)
463 @assignee.update_attribute(:mail_notification, :only_owner)
452 assert ! @assignee.notify_about?(@issue)
464 assert ! @assignee.notify_about?(@issue)
453 end
465 end
454
466
455 should "be false if the mail_notification is anything else" do
467 should "be false if the mail_notification is anything else" do
456 @assignee.update_attribute(:mail_notification, :somthing_else)
468 @assignee.update_attribute(:mail_notification, :somthing_else)
457 assert ! @assignee.notify_about?(@issue)
469 assert ! @assignee.notify_about?(@issue)
458 end
470 end
459
471
460 end
472 end
461
473
462 context "other events" do
474 context "other events" do
463 should 'be added and tested'
475 should 'be added and tested'
464 end
476 end
465 end
477 end
466
478
467 if Object.const_defined?(:OpenID)
479 if Object.const_defined?(:OpenID)
468
480
469 def test_setting_identity_url
481 def test_setting_identity_url
470 normalized_open_id_url = 'http://example.com/'
482 normalized_open_id_url = 'http://example.com/'
471 u = User.new( :identity_url => 'http://example.com/' )
483 u = User.new( :identity_url => 'http://example.com/' )
472 assert_equal normalized_open_id_url, u.identity_url
484 assert_equal normalized_open_id_url, u.identity_url
473 end
485 end
474
486
475 def test_setting_identity_url_without_trailing_slash
487 def test_setting_identity_url_without_trailing_slash
476 normalized_open_id_url = 'http://example.com/'
488 normalized_open_id_url = 'http://example.com/'
477 u = User.new( :identity_url => 'http://example.com' )
489 u = User.new( :identity_url => 'http://example.com' )
478 assert_equal normalized_open_id_url, u.identity_url
490 assert_equal normalized_open_id_url, u.identity_url
479 end
491 end
480
492
481 def test_setting_identity_url_without_protocol
493 def test_setting_identity_url_without_protocol
482 normalized_open_id_url = 'http://example.com/'
494 normalized_open_id_url = 'http://example.com/'
483 u = User.new( :identity_url => 'example.com' )
495 u = User.new( :identity_url => 'example.com' )
484 assert_equal normalized_open_id_url, u.identity_url
496 assert_equal normalized_open_id_url, u.identity_url
485 end
497 end
486
498
487 def test_setting_blank_identity_url
499 def test_setting_blank_identity_url
488 u = User.new( :identity_url => 'example.com' )
500 u = User.new( :identity_url => 'example.com' )
489 u.identity_url = ''
501 u.identity_url = ''
490 assert u.identity_url.blank?
502 assert u.identity_url.blank?
491 end
503 end
492
504
493 def test_setting_invalid_identity_url
505 def test_setting_invalid_identity_url
494 u = User.new( :identity_url => 'this is not an openid url' )
506 u = User.new( :identity_url => 'this is not an openid url' )
495 assert u.identity_url.blank?
507 assert u.identity_url.blank?
496 end
508 end
497
509
498 else
510 else
499 puts "Skipping openid tests."
511 puts "Skipping openid tests."
500 end
512 end
501
513
502 end
514 end
General Comments 0
You need to be logged in to leave comments. Login now