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