Index: test/unit/user_test.rb
===================================================================
--- test/unit/user_test.rb	(revision 1711)
+++ test/unit/user_test.rb	(working copy)
@@ -83,7 +83,13 @@
     Setting.user_format = :username
     assert_equal 'jsmith', @jsmith.name
   end
-  
+
+  def test_sort_format
+    assert_equal 'users.lastname ASC', User::USER_SORT_FORMATS[:lastname]
+    assert_equal 'users.firstname ASC', User::USER_SORT_FORMATS[:firstname]
+    assert_equal 'users.login ASC', User::USER_SORT_FORMATS[:username]
+  end
+
   def test_lock
     user = User.try_to_login("jsmith", "jsmith")
     assert_equal @jsmith, user
@@ -152,4 +158,13 @@
     @jsmith.pref.comments_sorting = 'desc'
     assert @jsmith.wants_comments_in_reverse_order?
   end
+  
+  def test_find_missing_members
+    # Project has members 2, 3, 5 (locked)
+    @project = Project.find(1)
+    missing_members = User.find_missing_members(@project)
+    # Users 1, 4 missing.  User 6 Anonymous
+    assert missing_members.include?(User.find(1))
+    assert missing_members.include?(User.find(4))
+  end
 end
Index: test/unit/project_test.rb
===================================================================
--- test/unit/project_test.rb	(revision 1711)
+++ test/unit/project_test.rb	(working copy)
@@ -130,4 +130,52 @@
     assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
     assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
   end
+  
+  def test_assignable_user
+    assert @ecookbook.assignable_users.include?(User.find(2))
+    assert @ecookbook.assignable_users.include?(User.find(3))
+  end
+
+  def test_assignable_user_sorted_by_lastname
+    Setting.user_sort = :lastname
+    new_user = create_user_for_sorting(@ecookbook)
+    
+    assert_equal User.find(3), @ecookbook.assignable_users[0] # Lopper
+    assert_equal User.find(2), @ecookbook.assignable_users[1] # Smith
+    assert_equal User.find(new_user.id), @ecookbook.assignable_users[2] # Zimmer
+  end
+
+  def test_assignable_user_sorted_by_firstname
+    Setting.user_sort = :firstname
+    new_user = create_user_for_sorting(@ecookbook)
+
+    assert_equal User.find(new_user.id), @ecookbook.assignable_users[0] # Alan
+    assert_equal User.find(3), @ecookbook.assignable_users[1] # Dave
+    assert_equal User.find(2), @ecookbook.assignable_users[2] # John
+  end
+
+  def test_assignable_user_sorted_by_username
+    Setting.user_sort = :username
+    new_user = create_user_for_sorting(@ecookbook)
+    
+    assert_equal User.find(3), @ecookbook.assignable_users[0] # dlopper
+    assert_equal User.find(new_user.id), @ecookbook.assignable_users[1] # good_user
+    assert_equal User.find(2), @ecookbook.assignable_users[2] # jsmith
+  end
+  
+  private
+  def create_user_for_sorting(project)
+    u = User.new(
+                 :firstname => "Alan",
+                 :lastname => "Zimmer",
+                 :mail => "newuser@somenet.foo",
+                 :password => 'password',
+                 :password_confirmation => 'password'
+                 )
+    u.login = 'good_user'
+    u.save!
+
+    Member.create(:project => project, :user => u, :role_id => 1)
+    return u
+  end
 end
Index: app/models/user.rb
===================================================================
--- app/models/user.rb	(revision 1711)
+++ app/models/user.rb	(working copy)
@@ -32,6 +32,12 @@
     :lastname_coma_firstname => '#{lastname}, #{firstname}',
     :username => '#{login}'
   }
+  
+  USER_SORT_FORMATS = { 
+    :lastname => "#{User.table_name}.lastname ASC",
+    :firstname => "#{User.table_name}.firstname ASC",
+    :username => "#{User.table_name}.login ASC"
+  }
 
   has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
   has_many :projects, :through => :memberships
@@ -115,7 +121,12 @@
   rescue => text
     raise text
   end
-	
+
+  # Given +project+ find all users who are not yet a member on the project
+  def self.find_missing_members(project)
+    self.find_active(:all, :order => (USER_SORT_FORMATS[Setting.user_sort] || USER_SORT_FORMATS[:lastname])) - project.users
+  end
+  
   # Return user's full name for display
   def name(formatter = nil)
     f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
Index: app/models/project.rb
===================================================================
--- app/models/project.rb	(revision 1711)
+++ app/models/project.rb	(working copy)
@@ -187,7 +187,7 @@
   
   # Users issues can be assigned to
   def assignable_users
-    members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
+    members.find(:all, :order => (User::USER_SORT_FORMATS[Setting.user_sort] || User::USER_SORT_FORMATS[:lastname])).select {|m| m.role.assignable?}.collect {|m| m.user}
   end
   
   # Returns the mail adresses of users that should be always notified on project events
Index: app/controllers/settings_controller.rb
===================================================================
--- app/controllers/settings_controller.rb	(revision 1711)
+++ app/controllers/settings_controller.rb	(working copy)
@@ -39,6 +39,7 @@
     end
     @options = {}
     @options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
+    @options[:user_sort] = User::USER_SORT_FORMATS.keys.collect {|f| [Inflector.humanize(f), f.to_s] }
     @deliveries = ActionMailer::Base.perform_deliveries
   end
   
Index: app/views/settings/_general.rhtml
===================================================================
--- app/views/settings/_general.rhtml	(revision 1711)
+++ app/views/settings/_general.rhtml	(working copy)
@@ -23,6 +23,9 @@
 <p><label><%= l(:setting_user_format) %></label>
 <%= select_tag 'settings[user_format]', options_for_select( @options[:user_format], Setting.user_format.to_s ) %></p>
 
+<p><label><%= l(:setting_user_sort) %></label>
+<%= select_tag 'settings[user_sort]', options_for_select( @options[:user_sort], Setting.user_sort.to_s ) %></p>
+
 <p><label><%= l(:setting_attachment_max_size) %></label>
 <%= text_field_tag 'settings[attachment_max_size]', Setting.attachment_max_size, :size => 6 %> KB</p>
 
Index: app/views/projects/settings/_members.rhtml
===================================================================
--- app/views/projects/settings/_members.rhtml	(revision 1711)
+++ app/views/projects/settings/_members.rhtml	(working copy)
@@ -1,6 +1,6 @@
 <%= error_messages_for 'member' %>
 <% roles = Role.find_all_givable %>
-<% users = User.find_active(:all).sort - @project.users %>
+<% users = User.find_missing_members(@project) %>
 <% # members sorted by role position
    members = @project.members.find(:all, :include => [:role, :user]).sort %>
    
Index: lang/en.yml
===================================================================
--- lang/en.yml	(revision 1711)
+++ lang/en.yml	(working copy)
@@ -218,6 +218,7 @@
 setting_enabled_scm: Enabled SCM
 setting_mail_handler_api_enabled: Enable WS for incoming emails
 setting_mail_handler_api_key: API key
+setting_user_sort: Sort users by
 
 project_module_issue_tracking: Issue tracking
 project_module_time_tracking: Time tracking
Index: config/settings.yml
===================================================================
--- config/settings.yml	(revision 1711)
+++ config/settings.yml	(working copy)
@@ -94,6 +94,9 @@
 user_format:
   default: :firstname_lastname
   format: symbol
+user_sort:
+  default: :lastname
+  format: symbol
 cross_project_issue_relations:
   default: 0
 notified_events:
