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