<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>db/migrate/20090319185505_add_disabled_to_user.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,6 +1,9 @@
 
 edge
 
+* Users cannot be directly deleted anymore. They are disabled in order to not break references and keep the correct history.
+  If you really want to delete a user you have to resort to the script/console or SQL for now. Fixes ticket #110.
+
 * Move gems to vendor/gems and update Capistrano to 2.5.5 and Net::SSH to 2.0.11
 
 * Update Rails to 2.2.2</diff>
      <filename>CHANGELOG.txt</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 class UsersController &lt; ApplicationController
-  before_filter :ensure_admin, :only =&gt; [:new, :destroy, :create]
+  before_filter :ensure_admin, :only =&gt; [:new, :destroy, :create, :enable]
   before_filter :ensure_admin_or_my_entry, :only =&gt; [:edit, :update]
 
   # GET /users
@@ -80,10 +80,10 @@ class UsersController &lt; ApplicationController
     @user = User.find(params[:id])
     
     if @user.admin? &amp;&amp; User.admin_count == 1
-      message = 'Can not delete last admin user.'
+      message = 'Can not disable last admin user.'
     else
-      @user.destroy
-      message = 'User was successfully deleted.'
+      @user.disable
+      message = 'User was successfully disabled.'
     end
 
     respond_to do |format|
@@ -92,6 +92,17 @@ class UsersController &lt; ApplicationController
       format.xml  { head :ok }
     end
   end
+  
+  def enable
+    @user = User.find(params[:id])
+    @user.enable
+    flash[:notice] = &quot;The user was enabled&quot;
+    
+    respond_to do |format|
+      format.html { redirect_to users_path }
+      format.xml  { head :ok }
+    end
+  end
 
   # GET /users/1/deployments
   # GET /users/1/deployments.xml</diff>
      <filename>app/controllers/users_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,6 +16,9 @@ class User &lt; ActiveRecord::Base
   validates_length_of       :email,    :within =&gt; 3..100
   validates_uniqueness_of   :login, :email, :case_sensitive =&gt; false
   before_save :encrypt_password
+  
+  named_scope :enabled, :conditions =&gt; {:disabled =&gt; nil}
+  named_scope :disabled, :conditions =&gt; &quot;disabled IS NOT NULL&quot;
     
   def validate_on_update
     if User.find(self.id).admin? &amp;&amp; !self.admin?
@@ -25,7 +28,7 @@ class User &lt; ActiveRecord::Base
   
   # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
   def self.authenticate(login, password)
-    u = find_by_login(login) # need to get the salt
+    u = find_by_login_and_disabled(login, nil) # need to get the salt
     u &amp;&amp; u.authenticated?(password) ? u : nil
   end
 
@@ -83,12 +86,24 @@ class User &lt; ActiveRecord::Base
   end
   
   def self.admin_count
-    count(:id, :conditions =&gt; ['admin = 1'])
+    count(:id, :conditions =&gt; ['admin = 1 AND disabled IS NULL'])
   end
   
   def recent_deployments(limit=3)
     self.deployments.find(:all, :limit =&gt; limit, :order =&gt; 'created_at DESC')
   end
+  
+  def disabled?
+    !self.disabled.blank?
+  end
+  
+  def disable
+    self.update_attribute(:disabled, Time.now)
+  end
+  
+  def enable
+    self.update_attribute(:disabled, nil)
+  end
 
   protected
     # before filter </diff>
      <filename>app/models/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,8 +9,9 @@
   &lt;table class=&quot;sortable&quot;&gt;
     &lt;tr&gt;
       &lt;th width=&quot;1%&quot;&gt;Login&lt;/th&gt;
-      &lt;th width=&quot;97%&quot;&gt;Email&lt;/th&gt;
+      &lt;th width=&quot;96%&quot;&gt;Email&lt;/th&gt;
       &lt;th width=&quot;1%&quot;&gt;Admin&lt;/th&gt;
+      &lt;th width=&quot;1%&quot;&gt;Disabled&lt;/th&gt;
       &lt;th width=&quot;1%&quot;&gt;Created At&lt;/th&gt;
     &lt;/tr&gt;
     &lt;% for user in @users %&gt;
@@ -18,6 +19,11 @@
         &lt;td nowrap&gt;&lt;%= link_to h(user.login), user_path(user) %&gt;&lt;/td&gt;
         &lt;td&gt;&lt;%=h user.email %&gt;&lt;/td&gt;
         &lt;td&gt;&lt;%=h user.admin? ? 'Admin' : '' %&gt;&lt;/td&gt;
+        &lt;td&gt;
+          &lt;% if user.disabled? %&gt;
+            &lt;span style=&quot;color:red&quot;&gt;disabled&lt;/span&gt;
+          &lt;% end %&gt;
+        &lt;/td&gt;
         &lt;td&gt;&lt;%=h user.created_at.to_s(:date_with_day) %&gt;&lt;/td&gt;
         &lt;% if current_user.admin? || current_user == user -%&gt;
           &lt;td&gt;&lt;%= link_to 'Edit', edit_user_path(user) %&gt;&lt;/td&gt;</diff>
      <filename>app/views/users/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -31,8 +31,12 @@
 			&lt;/p&gt;
 			&lt;br/&gt;
 			&lt;% if current_user.admin? || current_user == @user %&gt;
-			 &lt;%= link_to 'Edit &amp; change password', edit_user_path(@user), :class =&gt; 'arrow_link' %&gt; | 
-			 &lt;%= link_to 'Delete', user_path(@user), :confirm =&gt; 'Are you sure?', :method =&gt; :delete, :class =&gt; 'arrow_link'  %&gt;
+			  &lt;%= link_to 'Edit &amp; change password', edit_user_path(@user), :class =&gt; 'arrow_link' %&gt; | 
+			  &lt;% if @user.disabled? %&gt;
+			    &lt;%= link_to 'Enable', enable_user_path(@user), :confirm =&gt; 'Are you sure?', :method =&gt; :post, :class =&gt; 'arrow_link'  %&gt;
+			  &lt;% else %&gt;
+			    &lt;%= link_to 'Disable', user_path(@user), :confirm =&gt; 'Are you sure? Disabled users can no longer login.', :method =&gt; :delete, :class =&gt; 'arrow_link'  %&gt;
+			  &lt;% end %&gt;
 			&lt;% end %&gt;
   &lt;/div&gt;
   &lt;div class=&quot;box_bottom&quot;&gt;&lt;/div&gt;</diff>
      <filename>app/views/users/show.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -30,7 +30,7 @@ ActionController::Routing::Routes.draw do |map|
   end
   
   # RESTful auth
-  map.resources :users,:member =&gt; {:deployments =&gt; :get}
+  map.resources :users,:member =&gt; {:deployments =&gt; :get, :enable =&gt; :post}
   map.resources :sessions, :collection =&gt; {:version =&gt; :get}
   map.signup '/signup', :controller =&gt; 'users', :action =&gt; 'new'
   map.login  '/login', :controller =&gt; 'sessions', :action =&gt; 'new'</diff>
      <filename>config/routes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version =&gt; 20090223081938) do
+ActiveRecord::Schema.define(:version =&gt; 20090319185505) do
 
   create_table &quot;configuration_parameters&quot;, :force =&gt; true do |t|
     t.string   &quot;name&quot;
@@ -119,6 +119,9 @@ ActiveRecord::Schema.define(:version =&gt; 20090223081938) do
     t.datetime &quot;remember_token_expires_at&quot;
     t.integer  &quot;admin&quot;,                                   :default =&gt; 0
     t.string   &quot;time_zone&quot;,                               :default =&gt; &quot;UTC&quot;
+    t.datetime &quot;disabled&quot;
   end
 
+  add_index &quot;users&quot;, [&quot;disabled&quot;], :name =&gt; &quot;index_users_on_disabled&quot;
+
 end</diff>
      <filename>db/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,6 +22,14 @@ class SessionsControllerTest &lt; Test::Unit::TestCase
     assert session[:user]
     assert_response :redirect
   end
+  
+  def test_should_not_login_if_disabled
+    User.find_by_login('quentin').disable
+    
+    post :create, :login =&gt; 'quentin', :password =&gt; 'test'
+    assert_nil session[:user]
+    assert_response :success
+  end
 
   def test_should_fail_login_and_not_redirect
     post :create, :login =&gt; 'quentin', :password =&gt; 'bad password'</diff>
      <filename>test/functional/sessions_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -99,7 +99,7 @@ class UsersControllerTest &lt; Test::Unit::TestCase
     assert admin.admin?
     login(admin)
     delete :destroy, :id =&gt; user_2.id
-    assert_equal 3, User.count
+    assert_equal 3, User.enabled.count
   end
   
   def test_admin_status_can_not_be_set_by_non_admins
@@ -147,15 +147,15 @@ class UsersControllerTest &lt; Test::Unit::TestCase
     
     # delete the user
     delete :destroy, :id =&gt; user.id
-    assert_equal 2, User.count
+    assert_equal 2, User.enabled.count
     
     # delete the other admin
     delete :destroy, :id =&gt; admin_2.id
-    assert_equal 1, User.count
+    assert_equal 1, User.enabled.count
     
     # last admin can not be deleted
     delete :destroy, :id =&gt; admin.id
-    assert_equal 1, User.count
+    assert_equal 1, User.enabled.count
   end
   
   # basic non-exception test
@@ -191,6 +191,44 @@ class UsersControllerTest &lt; Test::Unit::TestCase
     assert_not_equal 'foobarrr', other.login
   end
   
+  def test_destroy_should_only_mark_as_disabled
+    user = admin_login
+    other = create_new_user
+    assert !other.disabled?
+    
+    assert_difference &quot;User.disabled.count&quot; do
+      assert_no_difference &quot;User.count&quot; do
+        post :destroy, :id =&gt; other.id
+        assert_response :redirect
+      end
+    end
+    
+  end
+  
+  def test_enable
+    user = admin_login
+    other = create_new_user
+    other.disable
+    
+    post :enable, :id =&gt; other.id
+    assert_response :redirect
+    
+    other.reload
+    assert !other.disabled?
+  end
+  
+  def test_enable_only_admin
+    user = login
+    other = create_new_user
+    other.disable
+    
+    post :enable, :id =&gt; other.id
+    assert_response :redirect
+    
+    other.reload
+    assert other.disabled?
+  end
+  
 
   protected
     def create_user(options = {})</diff>
      <filename>test/functional/users_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,12 @@ class UserTest &lt; Test::Unit::TestCase
       assert u.errors.on(:email)
     end
   end
+  
+  def test_should_not_authenticate_if_disabled
+    assert_equal users(:quentin), User.authenticate('quentin', 'test')
+    User.find_by_login(&quot;quentin&quot;).disable
+    assert_equal nil, User.authenticate('quentin', 'test')
+  end
 
   def test_should_reset_password
     users(:quentin).update_attributes(:password =&gt; 'new password', :password_confirmation =&gt; 'new password')
@@ -136,6 +142,35 @@ class UserTest &lt; Test::Unit::TestCase
     assert_equal 3, user.recent_deployments.size
     assert_equal 2, user.recent_deployments(2).size
   end
+  
+  def test_disable
+    user = create_new_user
+    assert !user.disabled?
+    
+    user.disable
+    
+    assert user.disabled?
+    
+    user.enable
+    
+    assert !user.disabled?
+  end
+  
+  def test_enabled_named_scope
+    User.destroy_all
+    assert_equal [], User.enabled
+    assert_equal [], User.disabled
+    
+    user = create_new_user
+    
+    assert_equal [user], User.enabled
+    assert_equal [], User.disabled
+    
+    user.disable
+    
+    assert_equal [], User.enabled
+    assert_equal [user], User.disabled
+  end
 
   protected
     def create_user(options = {})</diff>
      <filename>test/unit/user_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>cdbff3924bf11832a0e6e458ff8cca769933ca46</id>
    </parent>
  </parents>
  <author>
    <name>Jonathan Weiss</name>
    <email>jw@innerewut.de</email>
  </author>
  <url>http://github.com/peritor/webistrano/commit/519b8dab7b88102bc818afc0352012a9a1b87a62</url>
  <id>519b8dab7b88102bc818afc0352012a9a1b87a62</id>
  <committed-date>2009-03-19T12:24:46-07:00</committed-date>
  <authored-date>2009-03-19T12:24:46-07:00</authored-date>
  <message>Users cannot be directly deleted anymore. They are disabled in order to not break references and keep the correct history.
If you really want to delete a user you have to resort to the script/console or SQL for now. Fixes ticket #110</message>
  <tree>ac106ec76a9c1f4a6cb086cd17d6b8c3ba659080</tree>
  <committer>
    <name>Jonathan Weiss</name>
    <email>jw@innerewut.de</email>
  </committer>
</commit>
