##// END OF EJS Templates
Sort users by their display names so that user dropdown lists are sorted alphabetically (#2015)....
Jean-Philippe Lang -
r2008:6a8be88ad613
parent child
Show More
@@ -0,0 +1,5
1 <?xml version="1.0" encoding="UTF-8"?>
2 <loadpath>
3 <pathentry path="" type="src"/>
4 <pathentry path="org.rubypeople.rdt.launching.RUBY_CONTAINER" type="con"/>
5 </loadpath>
@@ -0,0 +1,18
1 <?xml version="1.0" encoding="UTF-8"?>
2 <projectDescription>
3 <name>trunk_fixes</name>
4 <comment></comment>
5 <projects>
6 </projects>
7 <buildSpec>
8 <buildCommand>
9 <name>org.rubypeople.rdt.core.rubybuilder</name>
10 <arguments>
11 </arguments>
12 </buildCommand>
13 </buildSpec>
14 <natures>
15 <nature>org.rubypeople.rdt.core.rubynature</nature>
16 <nature>org.radrails.rails.core.railsnature</nature>
17 </natures>
18 </projectDescription>
@@ -0,0 +1,5
1 #Sun Nov 09 21:42:24 CET 2008
2 eclipse.preferences.version=1
3 encoding//app/helpers/ifpdf_helper.rb=UTF-8
4 encoding//test/fixtures/mail_handler=UTF-8
5 encoding/lang=UTF-8
@@ -1,295 +1,294
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 < ActiveRecord::Base
21 21
22 22 # Account statuses
23 23 STATUS_ANONYMOUS = 0
24 24 STATUS_ACTIVE = 1
25 25 STATUS_REGISTERED = 2
26 26 STATUS_LOCKED = 3
27 27
28 28 USER_FORMATS = {
29 29 :firstname_lastname => '#{firstname} #{lastname}',
30 30 :firstname => '#{firstname}',
31 31 :lastname_firstname => '#{lastname} #{firstname}',
32 32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 33 :username => '#{login}'
34 34 }
35 35
36 36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
37 37 has_many :members, :dependent => :delete_all
38 38 has_many :projects, :through => :memberships
39 39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
40 40 has_many :changesets, :dependent => :nullify
41 41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
43 43 belongs_to :auth_source
44 44
45 45 acts_as_customizable
46 46
47 47 attr_accessor :password, :password_confirmation
48 48 attr_accessor :last_before_login_on
49 49 # Prevents unauthorized assignments
50 50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
51 51
52 52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
53 53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
54 54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
55 55 # Login must contain lettres, numbers, underscores only
56 56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
57 57 validates_length_of :login, :maximum => 30
58 58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
59 59 validates_length_of :firstname, :lastname, :maximum => 30
60 60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
61 61 validates_length_of :mail, :maximum => 60, :allow_nil => true
62 62 validates_length_of :password, :minimum => 4, :allow_nil => true
63 63 validates_confirmation_of :password, :allow_nil => true
64 64
65 65 def before_create
66 66 self.mail_notification = false
67 67 true
68 68 end
69 69
70 70 def before_save
71 71 # update hashed_password if password was set
72 72 self.hashed_password = User.hash_password(self.password) if self.password
73 73 end
74
75 def reload(*args)
76 @name = nil
77 super
78 end
74 79
75 80 def self.active
76 81 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
77 82 yield
78 83 end
79 84 end
80 85
81 86 def self.find_active(*args)
82 87 active do
83 88 find(*args)
84 89 end
85 90 end
86 91
87 92 # Returns the user that matches provided login and password, or nil
88 93 def self.try_to_login(login, password)
89 94 # Make sure no one can sign in with an empty password
90 95 return nil if password.to_s.empty?
91 96 user = find(:first, :conditions => ["login=?", login])
92 97 if user
93 98 # user is already in local database
94 99 return nil if !user.active?
95 100 if user.auth_source
96 101 # user has an external authentication method
97 102 return nil unless user.auth_source.authenticate(login, password)
98 103 else
99 104 # authentication with local password
100 105 return nil unless User.hash_password(password) == user.hashed_password
101 106 end
102 107 else
103 108 # user is not yet registered, try to authenticate with available sources
104 109 attrs = AuthSource.authenticate(login, password)
105 110 if attrs
106 111 user = new(*attrs)
107 112 user.login = login
108 113 user.language = Setting.default_language
109 114 if user.save
110 115 user.reload
111 116 logger.info("User '#{user.login}' created from the LDAP") if logger
112 117 end
113 118 end
114 119 end
115 120 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
116 121 user
117 122 rescue => text
118 123 raise text
119 124 end
120 125
121 126 # Return user's full name for display
122 127 def name(formatter = nil)
123 f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
124 eval '"' + f + '"'
128 @name ||= eval('"' + (USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
125 129 end
126 130
127 131 def active?
128 132 self.status == STATUS_ACTIVE
129 133 end
130 134
131 135 def registered?
132 136 self.status == STATUS_REGISTERED
133 137 end
134 138
135 139 def locked?
136 140 self.status == STATUS_LOCKED
137 141 end
138 142
139 143 def check_password?(clear_password)
140 144 User.hash_password(clear_password) == self.hashed_password
141 145 end
142 146
143 147 def pref
144 148 self.preference ||= UserPreference.new(:user => self)
145 149 end
146 150
147 151 def time_zone
148 152 @time_zone ||= (self.pref.time_zone.blank? ? nil : TimeZone[self.pref.time_zone])
149 153 end
150 154
151 155 def wants_comments_in_reverse_order?
152 156 self.pref[:comments_sorting] == 'desc'
153 157 end
154 158
155 159 # Return user's RSS key (a 40 chars long string), used to access feeds
156 160 def rss_key
157 161 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
158 162 token.value
159 163 end
160 164
161 165 # Return an array of project ids for which the user has explicitly turned mail notifications on
162 166 def notified_projects_ids
163 167 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
164 168 end
165 169
166 170 def notified_project_ids=(ids)
167 171 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
168 172 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
169 173 @notified_projects_ids = nil
170 174 notified_projects_ids
171 175 end
172 176
173 177 def self.find_by_rss_key(key)
174 178 token = Token.find_by_value(key)
175 179 token && token.user.active? ? token.user : nil
176 180 end
177 181
178 182 def self.find_by_autologin_key(key)
179 183 token = Token.find_by_action_and_value('autologin', key)
180 184 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
181 185 end
182 186
187 # Sort users by their display names
183 188 def <=>(user)
184 if user.nil?
185 -1
186 elsif lastname.to_s.downcase == user.lastname.to_s.downcase
187 firstname.to_s.downcase <=> user.firstname.to_s.downcase
188 else
189 lastname.to_s.downcase <=> user.lastname.to_s.downcase
190 end
189 self.to_s.downcase <=> user.to_s.downcase
191 190 end
192 191
193 192 def to_s
194 193 name
195 194 end
196 195
197 196 def logged?
198 197 true
199 198 end
200 199
201 200 def anonymous?
202 201 !logged?
203 202 end
204 203
205 204 # Return user's role for project
206 205 def role_for_project(project)
207 206 # No role on archived projects
208 207 return nil unless project && project.active?
209 208 if logged?
210 209 # Find project membership
211 210 membership = memberships.detect {|m| m.project_id == project.id}
212 211 if membership
213 212 membership.role
214 213 else
215 214 @role_non_member ||= Role.non_member
216 215 end
217 216 else
218 217 @role_anonymous ||= Role.anonymous
219 218 end
220 219 end
221 220
222 221 # Return true if the user is a member of project
223 222 def member_of?(project)
224 223 role_for_project(project).member?
225 224 end
226 225
227 226 # Return true if the user is allowed to do the specified action on project
228 227 # action can be:
229 228 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
230 229 # * a permission Symbol (eg. :edit_project)
231 230 def allowed_to?(action, project, options={})
232 231 if project
233 232 # No action allowed on archived projects
234 233 return false unless project.active?
235 234 # No action allowed on disabled modules
236 235 return false unless project.allows_to?(action)
237 236 # Admin users are authorized for anything else
238 237 return true if admin?
239 238
240 239 role = role_for_project(project)
241 240 return false unless role
242 241 role.allowed_to?(action) && (project.is_public? || role.member?)
243 242
244 243 elsif options[:global]
245 244 # authorize if user has at least one role that has this permission
246 245 roles = memberships.collect {|m| m.role}.uniq
247 246 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
248 247 else
249 248 false
250 249 end
251 250 end
252 251
253 252 def self.current=(user)
254 253 @current_user = user
255 254 end
256 255
257 256 def self.current
258 257 @current_user ||= User.anonymous
259 258 end
260 259
261 260 def self.anonymous
262 261 anonymous_user = AnonymousUser.find(:first)
263 262 if anonymous_user.nil?
264 263 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
265 264 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
266 265 end
267 266 anonymous_user
268 267 end
269 268
270 269 private
271 270 # Return password digest
272 271 def self.hash_password(clear_password)
273 272 Digest::SHA1.hexdigest(clear_password || "")
274 273 end
275 274 end
276 275
277 276 class AnonymousUser < User
278 277
279 278 def validate_on_create
280 279 # There should be only one AnonymousUser in the database
281 280 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
282 281 end
283 282
284 283 def available_custom_fields
285 284 []
286 285 end
287 286
288 287 # Overrides a few properties
289 288 def logged?; false end
290 289 def admin; false end
291 290 def name; 'Anonymous' end
292 291 def mail; nil end
293 292 def time_zone; nil end
294 293 def rss_key; nil end
295 294 end
@@ -1,161 +1,161
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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 File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class UserTest < Test::Unit::TestCase
21 21 fixtures :users, :members, :projects
22 22
23 23 def setup
24 24 @admin = User.find(1)
25 25 @jsmith = User.find(2)
26 26 @dlopper = User.find(3)
27 27 end
28 28
29 29 def test_truth
30 30 assert_kind_of User, @jsmith
31 31 end
32 32
33 33 def test_create
34 34 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
35 35
36 36 user.login = "jsmith"
37 37 user.password, user.password_confirmation = "password", "password"
38 38 # login uniqueness
39 39 assert !user.save
40 40 assert_equal 1, user.errors.count
41 41
42 42 user.login = "newuser"
43 43 user.password, user.password_confirmation = "passwd", "password"
44 44 # password confirmation
45 45 assert !user.save
46 46 assert_equal 1, user.errors.count
47 47
48 48 user.password, user.password_confirmation = "password", "password"
49 49 assert user.save
50 50 end
51 51
52 52 def test_update
53 53 assert_equal "admin", @admin.login
54 54 @admin.login = "john"
55 55 assert @admin.save, @admin.errors.full_messages.join("; ")
56 56 @admin.reload
57 57 assert_equal "john", @admin.login
58 58 end
59 59
60 60 def test_destroy
61 61 User.find(2).destroy
62 62 assert_nil User.find_by_id(2)
63 63 assert Member.find_all_by_user_id(2).empty?
64 64 end
65 65
66 66 def test_validate
67 67 @admin.login = ""
68 68 assert !@admin.save
69 69 assert_equal 1, @admin.errors.count
70 70 end
71 71
72 72 def test_password
73 73 user = User.try_to_login("admin", "admin")
74 74 assert_kind_of User, user
75 75 assert_equal "admin", user.login
76 76 user.password = "hello"
77 77 assert user.save
78 78
79 79 user = User.try_to_login("admin", "hello")
80 80 assert_kind_of User, user
81 81 assert_equal "admin", user.login
82 82 assert_equal User.hash_password("hello"), user.hashed_password
83 83 end
84 84
85 85 def test_name_format
86 86 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
87 87 Setting.user_format = :firstname_lastname
88 assert_equal 'John Smith', @jsmith.name
88 assert_equal 'John Smith', @jsmith.reload.name
89 89 Setting.user_format = :username
90 assert_equal 'jsmith', @jsmith.name
90 assert_equal 'jsmith', @jsmith.reload.name
91 91 end
92 92
93 93 def test_lock
94 94 user = User.try_to_login("jsmith", "jsmith")
95 95 assert_equal @jsmith, user
96 96
97 97 @jsmith.status = User::STATUS_LOCKED
98 98 assert @jsmith.save
99 99
100 100 user = User.try_to_login("jsmith", "jsmith")
101 101 assert_equal nil, user
102 102 end
103 103
104 104 def test_create_anonymous
105 105 AnonymousUser.delete_all
106 106 anon = User.anonymous
107 107 assert !anon.new_record?
108 108 assert_kind_of AnonymousUser, anon
109 109 end
110 110
111 111 def test_rss_key
112 112 assert_nil @jsmith.rss_token
113 113 key = @jsmith.rss_key
114 114 assert_equal 40, key.length
115 115
116 116 @jsmith.reload
117 117 assert_equal key, @jsmith.rss_key
118 118 end
119 119
120 120 def test_role_for_project
121 121 # user with a role
122 122 role = @jsmith.role_for_project(Project.find(1))
123 123 assert_kind_of Role, role
124 124 assert_equal "Manager", role.name
125 125
126 126 # user with no role
127 127 assert !@dlopper.role_for_project(Project.find(2)).member?
128 128 end
129 129
130 130 def test_mail_notification_all
131 131 @jsmith.mail_notification = true
132 132 @jsmith.notified_project_ids = []
133 133 @jsmith.save
134 134 @jsmith.reload
135 135 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
136 136 end
137 137
138 138 def test_mail_notification_selected
139 139 @jsmith.mail_notification = false
140 140 @jsmith.notified_project_ids = [1]
141 141 @jsmith.save
142 142 @jsmith.reload
143 143 assert Project.find(1).recipients.include?(@jsmith.mail)
144 144 end
145 145
146 146 def test_mail_notification_none
147 147 @jsmith.mail_notification = false
148 148 @jsmith.notified_project_ids = []
149 149 @jsmith.save
150 150 @jsmith.reload
151 151 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
152 152 end
153 153
154 154 def test_comments_sorting_preference
155 155 assert !@jsmith.wants_comments_in_reverse_order?
156 156 @jsmith.pref.comments_sorting = 'asc'
157 157 assert !@jsmith.wants_comments_in_reverse_order?
158 158 @jsmith.pref.comments_sorting = 'desc'
159 159 assert @jsmith.wants_comments_in_reverse_order?
160 160 end
161 161 end
General Comments 0
You need to be logged in to leave comments. Login now