##// END OF EJS Templates
Fixes #2170: user display format in application settings broken by r2010....
Jean-Philippe Lang -
r2029:7c1d8ac00b62
parent child
Show More
@@ -1,294 +1,298
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < ActiveRecord::Base
20 class User < ActiveRecord::Base
21
21
22 # Account statuses
22 # Account statuses
23 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
24 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
25 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
26 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
27
27
28 USER_FORMATS = {
28 USER_FORMATS = {
29 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :username => '#{login}'
33 :username => '#{login}'
34 }
34 }
35
35
36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
37 has_many :members, :dependent => :delete_all
37 has_many :members, :dependent => :delete_all
38 has_many :projects, :through => :memberships
38 has_many :projects, :through => :memberships
39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
40 has_many :changesets, :dependent => :nullify
40 has_many :changesets, :dependent => :nullify
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
43 belongs_to :auth_source
43 belongs_to :auth_source
44
44
45 acts_as_customizable
45 acts_as_customizable
46
46
47 attr_accessor :password, :password_confirmation
47 attr_accessor :password, :password_confirmation
48 attr_accessor :last_before_login_on
48 attr_accessor :last_before_login_on
49 # Prevents unauthorized assignments
49 # Prevents unauthorized assignments
50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
51
51
52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
55 # Login must contain lettres, numbers, underscores only
55 # Login must contain lettres, numbers, underscores only
56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
57 validates_length_of :login, :maximum => 30
57 validates_length_of :login, :maximum => 30
58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
59 validates_length_of :firstname, :lastname, :maximum => 30
59 validates_length_of :firstname, :lastname, :maximum => 30
60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
61 validates_length_of :mail, :maximum => 60, :allow_nil => true
61 validates_length_of :mail, :maximum => 60, :allow_nil => true
62 validates_length_of :password, :minimum => 4, :allow_nil => true
62 validates_length_of :password, :minimum => 4, :allow_nil => true
63 validates_confirmation_of :password, :allow_nil => true
63 validates_confirmation_of :password, :allow_nil => true
64
64
65 def before_create
65 def before_create
66 self.mail_notification = false
66 self.mail_notification = false
67 true
67 true
68 end
68 end
69
69
70 def before_save
70 def before_save
71 # update hashed_password if password was set
71 # update hashed_password if password was set
72 self.hashed_password = User.hash_password(self.password) if self.password
72 self.hashed_password = User.hash_password(self.password) if self.password
73 end
73 end
74
74
75 def reload(*args)
75 def reload(*args)
76 @name = nil
76 @name = nil
77 super
77 super
78 end
78 end
79
79
80 def self.active
80 def self.active
81 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
81 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
82 yield
82 yield
83 end
83 end
84 end
84 end
85
85
86 def self.find_active(*args)
86 def self.find_active(*args)
87 active do
87 active do
88 find(*args)
88 find(*args)
89 end
89 end
90 end
90 end
91
91
92 # Returns the user that matches provided login and password, or nil
92 # Returns the user that matches provided login and password, or nil
93 def self.try_to_login(login, password)
93 def self.try_to_login(login, password)
94 # Make sure no one can sign in with an empty password
94 # Make sure no one can sign in with an empty password
95 return nil if password.to_s.empty?
95 return nil if password.to_s.empty?
96 user = find(:first, :conditions => ["login=?", login])
96 user = find(:first, :conditions => ["login=?", login])
97 if user
97 if user
98 # user is already in local database
98 # user is already in local database
99 return nil if !user.active?
99 return nil if !user.active?
100 if user.auth_source
100 if user.auth_source
101 # user has an external authentication method
101 # user has an external authentication method
102 return nil unless user.auth_source.authenticate(login, password)
102 return nil unless user.auth_source.authenticate(login, password)
103 else
103 else
104 # authentication with local password
104 # authentication with local password
105 return nil unless User.hash_password(password) == user.hashed_password
105 return nil unless User.hash_password(password) == user.hashed_password
106 end
106 end
107 else
107 else
108 # user is not yet registered, try to authenticate with available sources
108 # user is not yet registered, try to authenticate with available sources
109 attrs = AuthSource.authenticate(login, password)
109 attrs = AuthSource.authenticate(login, password)
110 if attrs
110 if attrs
111 user = new(*attrs)
111 user = new(*attrs)
112 user.login = login
112 user.login = login
113 user.language = Setting.default_language
113 user.language = Setting.default_language
114 if user.save
114 if user.save
115 user.reload
115 user.reload
116 logger.info("User '#{user.login}' created from the LDAP") if logger
116 logger.info("User '#{user.login}' created from the LDAP") if logger
117 end
117 end
118 end
118 end
119 end
119 end
120 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
120 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
121 user
121 user
122 rescue => text
122 rescue => text
123 raise text
123 raise text
124 end
124 end
125
125
126 # Return user's full name for display
126 # Return user's full name for display
127 def name(formatter = nil)
127 def name(formatter = nil)
128 @name ||= eval('"' + (USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
128 if formatter
129 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
130 else
131 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
132 end
129 end
133 end
130
134
131 def active?
135 def active?
132 self.status == STATUS_ACTIVE
136 self.status == STATUS_ACTIVE
133 end
137 end
134
138
135 def registered?
139 def registered?
136 self.status == STATUS_REGISTERED
140 self.status == STATUS_REGISTERED
137 end
141 end
138
142
139 def locked?
143 def locked?
140 self.status == STATUS_LOCKED
144 self.status == STATUS_LOCKED
141 end
145 end
142
146
143 def check_password?(clear_password)
147 def check_password?(clear_password)
144 User.hash_password(clear_password) == self.hashed_password
148 User.hash_password(clear_password) == self.hashed_password
145 end
149 end
146
150
147 def pref
151 def pref
148 self.preference ||= UserPreference.new(:user => self)
152 self.preference ||= UserPreference.new(:user => self)
149 end
153 end
150
154
151 def time_zone
155 def time_zone
152 @time_zone ||= (self.pref.time_zone.blank? ? nil : TimeZone[self.pref.time_zone])
156 @time_zone ||= (self.pref.time_zone.blank? ? nil : TimeZone[self.pref.time_zone])
153 end
157 end
154
158
155 def wants_comments_in_reverse_order?
159 def wants_comments_in_reverse_order?
156 self.pref[:comments_sorting] == 'desc'
160 self.pref[:comments_sorting] == 'desc'
157 end
161 end
158
162
159 # Return user's RSS key (a 40 chars long string), used to access feeds
163 # Return user's RSS key (a 40 chars long string), used to access feeds
160 def rss_key
164 def rss_key
161 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
165 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
162 token.value
166 token.value
163 end
167 end
164
168
165 # Return an array of project ids for which the user has explicitly turned mail notifications on
169 # Return an array of project ids for which the user has explicitly turned mail notifications on
166 def notified_projects_ids
170 def notified_projects_ids
167 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
171 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
168 end
172 end
169
173
170 def notified_project_ids=(ids)
174 def notified_project_ids=(ids)
171 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
175 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
172 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
176 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
173 @notified_projects_ids = nil
177 @notified_projects_ids = nil
174 notified_projects_ids
178 notified_projects_ids
175 end
179 end
176
180
177 def self.find_by_rss_key(key)
181 def self.find_by_rss_key(key)
178 token = Token.find_by_value(key)
182 token = Token.find_by_value(key)
179 token && token.user.active? ? token.user : nil
183 token && token.user.active? ? token.user : nil
180 end
184 end
181
185
182 def self.find_by_autologin_key(key)
186 def self.find_by_autologin_key(key)
183 token = Token.find_by_action_and_value('autologin', key)
187 token = Token.find_by_action_and_value('autologin', key)
184 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
188 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
185 end
189 end
186
190
187 # Sort users by their display names
191 # Sort users by their display names
188 def <=>(user)
192 def <=>(user)
189 self.to_s.downcase <=> user.to_s.downcase
193 self.to_s.downcase <=> user.to_s.downcase
190 end
194 end
191
195
192 def to_s
196 def to_s
193 name
197 name
194 end
198 end
195
199
196 def logged?
200 def logged?
197 true
201 true
198 end
202 end
199
203
200 def anonymous?
204 def anonymous?
201 !logged?
205 !logged?
202 end
206 end
203
207
204 # Return user's role for project
208 # Return user's role for project
205 def role_for_project(project)
209 def role_for_project(project)
206 # No role on archived projects
210 # No role on archived projects
207 return nil unless project && project.active?
211 return nil unless project && project.active?
208 if logged?
212 if logged?
209 # Find project membership
213 # Find project membership
210 membership = memberships.detect {|m| m.project_id == project.id}
214 membership = memberships.detect {|m| m.project_id == project.id}
211 if membership
215 if membership
212 membership.role
216 membership.role
213 else
217 else
214 @role_non_member ||= Role.non_member
218 @role_non_member ||= Role.non_member
215 end
219 end
216 else
220 else
217 @role_anonymous ||= Role.anonymous
221 @role_anonymous ||= Role.anonymous
218 end
222 end
219 end
223 end
220
224
221 # Return true if the user is a member of project
225 # Return true if the user is a member of project
222 def member_of?(project)
226 def member_of?(project)
223 role_for_project(project).member?
227 role_for_project(project).member?
224 end
228 end
225
229
226 # Return true if the user is allowed to do the specified action on project
230 # Return true if the user is allowed to do the specified action on project
227 # action can be:
231 # action can be:
228 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
232 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
229 # * a permission Symbol (eg. :edit_project)
233 # * a permission Symbol (eg. :edit_project)
230 def allowed_to?(action, project, options={})
234 def allowed_to?(action, project, options={})
231 if project
235 if project
232 # No action allowed on archived projects
236 # No action allowed on archived projects
233 return false unless project.active?
237 return false unless project.active?
234 # No action allowed on disabled modules
238 # No action allowed on disabled modules
235 return false unless project.allows_to?(action)
239 return false unless project.allows_to?(action)
236 # Admin users are authorized for anything else
240 # Admin users are authorized for anything else
237 return true if admin?
241 return true if admin?
238
242
239 role = role_for_project(project)
243 role = role_for_project(project)
240 return false unless role
244 return false unless role
241 role.allowed_to?(action) && (project.is_public? || role.member?)
245 role.allowed_to?(action) && (project.is_public? || role.member?)
242
246
243 elsif options[:global]
247 elsif options[:global]
244 # authorize if user has at least one role that has this permission
248 # authorize if user has at least one role that has this permission
245 roles = memberships.collect {|m| m.role}.uniq
249 roles = memberships.collect {|m| m.role}.uniq
246 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
250 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
247 else
251 else
248 false
252 false
249 end
253 end
250 end
254 end
251
255
252 def self.current=(user)
256 def self.current=(user)
253 @current_user = user
257 @current_user = user
254 end
258 end
255
259
256 def self.current
260 def self.current
257 @current_user ||= User.anonymous
261 @current_user ||= User.anonymous
258 end
262 end
259
263
260 def self.anonymous
264 def self.anonymous
261 anonymous_user = AnonymousUser.find(:first)
265 anonymous_user = AnonymousUser.find(:first)
262 if anonymous_user.nil?
266 if anonymous_user.nil?
263 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
267 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
264 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
268 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
265 end
269 end
266 anonymous_user
270 anonymous_user
267 end
271 end
268
272
269 private
273 private
270 # Return password digest
274 # Return password digest
271 def self.hash_password(clear_password)
275 def self.hash_password(clear_password)
272 Digest::SHA1.hexdigest(clear_password || "")
276 Digest::SHA1.hexdigest(clear_password || "")
273 end
277 end
274 end
278 end
275
279
276 class AnonymousUser < User
280 class AnonymousUser < User
277
281
278 def validate_on_create
282 def validate_on_create
279 # There should be only one AnonymousUser in the database
283 # There should be only one AnonymousUser in the database
280 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
284 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
281 end
285 end
282
286
283 def available_custom_fields
287 def available_custom_fields
284 []
288 []
285 end
289 end
286
290
287 # Overrides a few properties
291 # Overrides a few properties
288 def logged?; false end
292 def logged?; false end
289 def admin; false end
293 def admin; false end
290 def name; 'Anonymous' end
294 def name; 'Anonymous' end
291 def mail; nil end
295 def mail; nil end
292 def time_zone; nil end
296 def time_zone; nil end
293 def rss_key; nil end
297 def rss_key; nil end
294 end
298 end
General Comments 0
You need to be logged in to leave comments. Login now