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