##// END OF EJS Templates
fix typo at app/models/user.rb (#5719)...
Toshi MARUYAMA -
r11197:f76dcf33effb
parent child
Show More
@@ -1,686 +1,686
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 include Redmine::SafeAttributes
22 22
23 23 # Different ways of displaying/sorting users
24 24 USER_FORMATS = {
25 25 :firstname_lastname => {
26 26 :string => '#{firstname} #{lastname}',
27 27 :order => %w(firstname lastname id),
28 28 :setting_order => 1
29 29 },
30 30 :firstname_lastinitial => {
31 31 :string => '#{firstname} #{lastname.to_s.chars.first}.',
32 32 :order => %w(firstname lastname id),
33 33 :setting_order => 2
34 34 },
35 35 :firstname => {
36 36 :string => '#{firstname}',
37 37 :order => %w(firstname id),
38 38 :setting_order => 3
39 39 },
40 40 :lastname_firstname => {
41 41 :string => '#{lastname} #{firstname}',
42 42 :order => %w(lastname firstname id),
43 43 :setting_order => 4
44 44 },
45 45 :lastname_coma_firstname => {
46 46 :string => '#{lastname}, #{firstname}',
47 47 :order => %w(lastname firstname id),
48 48 :setting_order => 5
49 49 },
50 50 :lastname => {
51 51 :string => '#{lastname}',
52 52 :order => %w(lastname id),
53 53 :setting_order => 6
54 54 },
55 55 :username => {
56 56 :string => '#{login}',
57 57 :order => %w(login id),
58 58 :setting_order => 7
59 59 },
60 60 }
61 61
62 62 MAIL_NOTIFICATION_OPTIONS = [
63 63 ['all', :label_user_mail_option_all],
64 64 ['selected', :label_user_mail_option_selected],
65 65 ['only_my_events', :label_user_mail_option_only_my_events],
66 66 ['only_assigned', :label_user_mail_option_only_assigned],
67 67 ['only_owner', :label_user_mail_option_only_owner],
68 68 ['none', :label_user_mail_option_none]
69 69 ]
70 70
71 71 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
72 72 :after_remove => Proc.new {|user, group| group.user_removed(user)}
73 73 has_many :changesets, :dependent => :nullify
74 74 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
75 75 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
76 76 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
77 77 belongs_to :auth_source
78 78
79 79 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
80 80 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
81 81
82 82 acts_as_customizable
83 83
84 84 attr_accessor :password, :password_confirmation
85 85 attr_accessor :last_before_login_on
86 86 # Prevents unauthorized assignments
87 87 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
88 88
89 89 LOGIN_LENGTH_LIMIT = 60
90 90 MAIL_LENGTH_LIMIT = 60
91 91
92 92 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
93 93 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
94 94 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
95 # Login must contain lettres, numbers, underscores only
95 # Login must contain letters, numbers, underscores only
96 96 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
97 97 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
98 98 validates_length_of :firstname, :lastname, :maximum => 30
99 99 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
100 100 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
101 101 validates_confirmation_of :password, :allow_nil => true
102 102 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
103 103 validate :validate_password_length
104 104
105 105 before_create :set_mail_notification
106 106 before_save :update_hashed_password
107 107 before_destroy :remove_references_before_destroy
108 108
109 109 scope :in_group, lambda {|group|
110 110 group_id = group.is_a?(Group) ? group.id : group.to_i
111 111 where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
112 112 }
113 113 scope :not_in_group, lambda {|group|
114 114 group_id = group.is_a?(Group) ? group.id : group.to_i
115 115 where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
116 116 }
117 117 scope :sorted, lambda { order(*User.fields_for_order_statement)}
118 118
119 119 def set_mail_notification
120 120 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
121 121 true
122 122 end
123 123
124 124 def update_hashed_password
125 125 # update hashed_password if password was set
126 126 if self.password && self.auth_source_id.blank?
127 127 salt_password(password)
128 128 end
129 129 end
130 130
131 131 def reload(*args)
132 132 @name = nil
133 133 @projects_by_role = nil
134 134 super
135 135 end
136 136
137 137 def mail=(arg)
138 138 write_attribute(:mail, arg.to_s.strip)
139 139 end
140 140
141 141 def identity_url=(url)
142 142 if url.blank?
143 143 write_attribute(:identity_url, '')
144 144 else
145 145 begin
146 146 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
147 147 rescue OpenIdAuthentication::InvalidOpenId
148 # Invlaid url, don't save
148 # Invalid url, don't save
149 149 end
150 150 end
151 151 self.read_attribute(:identity_url)
152 152 end
153 153
154 154 # Returns the user that matches provided login and password, or nil
155 155 def self.try_to_login(login, password)
156 156 login = login.to_s
157 157 password = password.to_s
158 158
159 159 # Make sure no one can sign in with an empty login or password
160 160 return nil if login.empty? || password.empty?
161 161 user = find_by_login(login)
162 162 if user
163 163 # user is already in local database
164 164 return nil unless user.active?
165 165 return nil unless user.check_password?(password)
166 166 else
167 167 # user is not yet registered, try to authenticate with available sources
168 168 attrs = AuthSource.authenticate(login, password)
169 169 if attrs
170 170 user = new(attrs)
171 171 user.login = login
172 172 user.language = Setting.default_language
173 173 if user.save
174 174 user.reload
175 175 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
176 176 end
177 177 end
178 178 end
179 179 user.update_column(:last_login_on, Time.now) if user && !user.new_record?
180 180 user
181 181 rescue => text
182 182 raise text
183 183 end
184 184
185 185 # Returns the user who matches the given autologin +key+ or nil
186 186 def self.try_to_autologin(key)
187 187 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
188 188 if user
189 189 user.update_column(:last_login_on, Time.now)
190 190 user
191 191 end
192 192 end
193 193
194 194 def self.name_formatter(formatter = nil)
195 195 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
196 196 end
197 197
198 198 # Returns an array of fields names than can be used to make an order statement for users
199 199 # according to how user names are displayed
200 200 # Examples:
201 201 #
202 202 # User.fields_for_order_statement => ['users.login', 'users.id']
203 203 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
204 204 def self.fields_for_order_statement(table=nil)
205 205 table ||= table_name
206 206 name_formatter[:order].map {|field| "#{table}.#{field}"}
207 207 end
208 208
209 209 # Return user's full name for display
210 210 def name(formatter = nil)
211 211 f = self.class.name_formatter(formatter)
212 212 if formatter
213 213 eval('"' + f[:string] + '"')
214 214 else
215 215 @name ||= eval('"' + f[:string] + '"')
216 216 end
217 217 end
218 218
219 219 def active?
220 220 self.status == STATUS_ACTIVE
221 221 end
222 222
223 223 def registered?
224 224 self.status == STATUS_REGISTERED
225 225 end
226 226
227 227 def locked?
228 228 self.status == STATUS_LOCKED
229 229 end
230 230
231 231 def activate
232 232 self.status = STATUS_ACTIVE
233 233 end
234 234
235 235 def register
236 236 self.status = STATUS_REGISTERED
237 237 end
238 238
239 239 def lock
240 240 self.status = STATUS_LOCKED
241 241 end
242 242
243 243 def activate!
244 244 update_attribute(:status, STATUS_ACTIVE)
245 245 end
246 246
247 247 def register!
248 248 update_attribute(:status, STATUS_REGISTERED)
249 249 end
250 250
251 251 def lock!
252 252 update_attribute(:status, STATUS_LOCKED)
253 253 end
254 254
255 255 # Returns true if +clear_password+ is the correct user's password, otherwise false
256 256 def check_password?(clear_password)
257 257 if auth_source_id.present?
258 258 auth_source.authenticate(self.login, clear_password)
259 259 else
260 260 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
261 261 end
262 262 end
263 263
264 264 # Generates a random salt and computes hashed_password for +clear_password+
265 265 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
266 266 def salt_password(clear_password)
267 267 self.salt = User.generate_salt
268 268 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
269 269 end
270 270
271 271 # Does the backend storage allow this user to change their password?
272 272 def change_password_allowed?
273 273 return true if auth_source.nil?
274 274 return auth_source.allow_password_changes?
275 275 end
276 276
277 277 # Generate and set a random password. Useful for automated user creation
278 278 # Based on Token#generate_token_value
279 279 #
280 280 def random_password
281 281 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
282 282 password = ''
283 283 40.times { |i| password << chars[rand(chars.size-1)] }
284 284 self.password = password
285 285 self.password_confirmation = password
286 286 self
287 287 end
288 288
289 289 def pref
290 290 self.preference ||= UserPreference.new(:user => self)
291 291 end
292 292
293 293 def time_zone
294 294 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
295 295 end
296 296
297 297 def wants_comments_in_reverse_order?
298 298 self.pref[:comments_sorting] == 'desc'
299 299 end
300 300
301 301 # Return user's RSS key (a 40 chars long string), used to access feeds
302 302 def rss_key
303 303 if rss_token.nil?
304 304 create_rss_token(:action => 'feeds')
305 305 end
306 306 rss_token.value
307 307 end
308 308
309 309 # Return user's API key (a 40 chars long string), used to access the API
310 310 def api_key
311 311 if api_token.nil?
312 312 create_api_token(:action => 'api')
313 313 end
314 314 api_token.value
315 315 end
316 316
317 317 # Return an array of project ids for which the user has explicitly turned mail notifications on
318 318 def notified_projects_ids
319 319 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
320 320 end
321 321
322 322 def notified_project_ids=(ids)
323 323 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
324 324 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
325 325 @notified_projects_ids = nil
326 326 notified_projects_ids
327 327 end
328 328
329 329 def valid_notification_options
330 330 self.class.valid_notification_options(self)
331 331 end
332 332
333 333 # Only users that belong to more than 1 project can select projects for which they are notified
334 334 def self.valid_notification_options(user=nil)
335 335 # Note that @user.membership.size would fail since AR ignores
336 336 # :include association option when doing a count
337 337 if user.nil? || user.memberships.length < 1
338 338 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
339 339 else
340 340 MAIL_NOTIFICATION_OPTIONS
341 341 end
342 342 end
343 343
344 344 # Find a user account by matching the exact login and then a case-insensitive
345 345 # version. Exact matches will be given priority.
346 346 def self.find_by_login(login)
347 347 if login.present?
348 348 login = login.to_s
349 349 # First look for an exact match
350 350 user = where(:login => login).all.detect {|u| u.login == login}
351 351 unless user
352 352 # Fail over to case-insensitive if none was found
353 353 user = where("LOWER(login) = ?", login.downcase).first
354 354 end
355 355 user
356 356 end
357 357 end
358 358
359 359 def self.find_by_rss_key(key)
360 360 Token.find_active_user('feeds', key)
361 361 end
362 362
363 363 def self.find_by_api_key(key)
364 364 Token.find_active_user('api', key)
365 365 end
366 366
367 367 # Makes find_by_mail case-insensitive
368 368 def self.find_by_mail(mail)
369 369 where("LOWER(mail) = ?", mail.to_s.downcase).first
370 370 end
371 371
372 372 # Returns true if the default admin account can no longer be used
373 373 def self.default_admin_account_changed?
374 374 !User.active.find_by_login("admin").try(:check_password?, "admin")
375 375 end
376 376
377 377 def to_s
378 378 name
379 379 end
380 380
381 381 CSS_CLASS_BY_STATUS = {
382 382 STATUS_ANONYMOUS => 'anon',
383 383 STATUS_ACTIVE => 'active',
384 384 STATUS_REGISTERED => 'registered',
385 385 STATUS_LOCKED => 'locked'
386 386 }
387 387
388 388 def css_classes
389 389 "user #{CSS_CLASS_BY_STATUS[status]}"
390 390 end
391 391
392 392 # Returns the current day according to user's time zone
393 393 def today
394 394 if time_zone.nil?
395 395 Date.today
396 396 else
397 397 Time.now.in_time_zone(time_zone).to_date
398 398 end
399 399 end
400 400
401 401 # Returns the day of +time+ according to user's time zone
402 402 def time_to_date(time)
403 403 if time_zone.nil?
404 404 time.to_date
405 405 else
406 406 time.in_time_zone(time_zone).to_date
407 407 end
408 408 end
409 409
410 410 def logged?
411 411 true
412 412 end
413 413
414 414 def anonymous?
415 415 !logged?
416 416 end
417 417
418 418 # Return user's roles for project
419 419 def roles_for_project(project)
420 420 roles = []
421 421 # No role on archived projects
422 422 return roles if project.nil? || project.archived?
423 423 if logged?
424 424 # Find project membership
425 425 membership = memberships.detect {|m| m.project_id == project.id}
426 426 if membership
427 427 roles = membership.roles
428 428 else
429 429 @role_non_member ||= Role.non_member
430 430 roles << @role_non_member
431 431 end
432 432 else
433 433 @role_anonymous ||= Role.anonymous
434 434 roles << @role_anonymous
435 435 end
436 436 roles
437 437 end
438 438
439 439 # Return true if the user is a member of project
440 440 def member_of?(project)
441 441 roles_for_project(project).any? {|role| role.member?}
442 442 end
443 443
444 444 # Returns a hash of user's projects grouped by roles
445 445 def projects_by_role
446 446 return @projects_by_role if @projects_by_role
447 447
448 448 @projects_by_role = Hash.new([])
449 449 memberships.each do |membership|
450 450 if membership.project
451 451 membership.roles.each do |role|
452 452 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
453 453 @projects_by_role[role] << membership.project
454 454 end
455 455 end
456 456 end
457 457 @projects_by_role.each do |role, projects|
458 458 projects.uniq!
459 459 end
460 460
461 461 @projects_by_role
462 462 end
463 463
464 464 # Returns true if user is arg or belongs to arg
465 465 def is_or_belongs_to?(arg)
466 466 if arg.is_a?(User)
467 467 self == arg
468 468 elsif arg.is_a?(Group)
469 469 arg.users.include?(self)
470 470 else
471 471 false
472 472 end
473 473 end
474 474
475 475 # Return true if the user is allowed to do the specified action on a specific context
476 476 # Action can be:
477 477 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
478 478 # * a permission Symbol (eg. :edit_project)
479 479 # Context can be:
480 480 # * a project : returns true if user is allowed to do the specified action on this project
481 481 # * an array of projects : returns true if user is allowed on every project
482 482 # * nil with options[:global] set : check if user has at least one role allowed for this action,
483 483 # or falls back to Non Member / Anonymous permissions depending if the user is logged
484 484 def allowed_to?(action, context, options={}, &block)
485 485 if context && context.is_a?(Project)
486 486 return false unless context.allows_to?(action)
487 487 # Admin users are authorized for anything else
488 488 return true if admin?
489 489
490 490 roles = roles_for_project(context)
491 491 return false unless roles
492 492 roles.any? {|role|
493 493 (context.is_public? || role.member?) &&
494 494 role.allowed_to?(action) &&
495 495 (block_given? ? yield(role, self) : true)
496 496 }
497 497 elsif context && context.is_a?(Array)
498 498 if context.empty?
499 499 false
500 500 else
501 501 # Authorize if user is authorized on every element of the array
502 502 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
503 503 end
504 504 elsif options[:global]
505 505 # Admin users are always authorized
506 506 return true if admin?
507 507
508 508 # authorize if user has at least one role that has this permission
509 509 roles = memberships.collect {|m| m.roles}.flatten.uniq
510 510 roles << (self.logged? ? Role.non_member : Role.anonymous)
511 511 roles.any? {|role|
512 512 role.allowed_to?(action) &&
513 513 (block_given? ? yield(role, self) : true)
514 514 }
515 515 else
516 516 false
517 517 end
518 518 end
519 519
520 520 # Is the user allowed to do the specified action on any project?
521 521 # See allowed_to? for the actions and valid options.
522 522 def allowed_to_globally?(action, options, &block)
523 523 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
524 524 end
525 525
526 526 # Returns true if the user is allowed to delete his own account
527 527 def own_account_deletable?
528 528 Setting.unsubscribe? &&
529 529 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
530 530 end
531 531
532 532 safe_attributes 'login',
533 533 'firstname',
534 534 'lastname',
535 535 'mail',
536 536 'mail_notification',
537 537 'language',
538 538 'custom_field_values',
539 539 'custom_fields',
540 540 'identity_url'
541 541
542 542 safe_attributes 'status',
543 543 'auth_source_id',
544 544 :if => lambda {|user, current_user| current_user.admin?}
545 545
546 546 safe_attributes 'group_ids',
547 547 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
548 548
549 549 # Utility method to help check if a user should be notified about an
550 550 # event.
551 551 #
552 552 # TODO: only supports Issue events currently
553 553 def notify_about?(object)
554 554 if mail_notification == 'all'
555 555 true
556 556 elsif mail_notification.blank? || mail_notification == 'none'
557 557 false
558 558 else
559 559 case object
560 560 when Issue
561 561 case mail_notification
562 562 when 'selected', 'only_my_events'
563 563 # user receives notifications for created/assigned issues on unselected projects
564 564 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
565 565 when 'only_assigned'
566 566 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
567 567 when 'only_owner'
568 568 object.author == self
569 569 end
570 570 when News
571 571 # always send to project members except when mail_notification is set to 'none'
572 572 true
573 573 end
574 574 end
575 575 end
576 576
577 577 def self.current=(user)
578 578 Thread.current[:current_user] = user
579 579 end
580 580
581 581 def self.current
582 582 Thread.current[:current_user] ||= User.anonymous
583 583 end
584 584
585 585 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
586 586 # one anonymous user per database.
587 587 def self.anonymous
588 588 anonymous_user = AnonymousUser.first
589 589 if anonymous_user.nil?
590 590 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
591 591 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
592 592 end
593 593 anonymous_user
594 594 end
595 595
596 596 # Salts all existing unsalted passwords
597 597 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
598 598 # This method is used in the SaltPasswords migration and is to be kept as is
599 599 def self.salt_unsalted_passwords!
600 600 transaction do
601 601 User.where("salt IS NULL OR salt = ''").find_each do |user|
602 602 next if user.hashed_password.blank?
603 603 salt = User.generate_salt
604 604 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
605 605 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
606 606 end
607 607 end
608 608 end
609 609
610 610 protected
611 611
612 612 def validate_password_length
613 613 # Password length validation based on setting
614 614 if !password.nil? && password.size < Setting.password_min_length.to_i
615 615 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
616 616 end
617 617 end
618 618
619 619 private
620 620
621 621 # Removes references that are not handled by associations
622 622 # Things that are not deleted are reassociated with the anonymous user
623 623 def remove_references_before_destroy
624 624 return if self.id.nil?
625 625
626 626 substitute = User.anonymous
627 627 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
628 628 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
629 629 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
630 630 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
631 631 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
632 632 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
633 633 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
634 634 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
635 635 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
636 636 # Remove private queries and keep public ones
637 637 ::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
638 638 ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
639 639 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
640 640 Token.delete_all ['user_id = ?', id]
641 641 Watcher.delete_all ['user_id = ?', id]
642 642 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
643 643 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
644 644 end
645 645
646 646 # Return password digest
647 647 def self.hash_password(clear_password)
648 648 Digest::SHA1.hexdigest(clear_password || "")
649 649 end
650 650
651 651 # Returns a 128bits random salt as a hex string (32 chars long)
652 652 def self.generate_salt
653 653 Redmine::Utils.random_hex(16)
654 654 end
655 655
656 656 end
657 657
658 658 class AnonymousUser < User
659 659 validate :validate_anonymous_uniqueness, :on => :create
660 660
661 661 def validate_anonymous_uniqueness
662 662 # There should be only one AnonymousUser in the database
663 663 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
664 664 end
665 665
666 666 def available_custom_fields
667 667 []
668 668 end
669 669
670 670 # Overrides a few properties
671 671 def logged?; false end
672 672 def admin; false end
673 673 def name(*args); I18n.t(:label_user_anonymous) end
674 674 def mail; nil end
675 675 def time_zone; nil end
676 676 def rss_key; nil end
677 677
678 678 def pref
679 679 UserPreference.new(:user => self)
680 680 end
681 681
682 682 # Anonymous user can not be destroyed
683 683 def destroy
684 684 false
685 685 end
686 686 end
General Comments 0
You need to be logged in to leave comments. Login now