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