Skip to content

Commit

Permalink
Merge pull request hotsh#620 from carols10cents/account_deletion
Browse files Browse the repository at this point in the history
Account deletion
  • Loading branch information
steveklabnik committed Sep 3, 2012
2 parents 1f16095 + 07e7a59 commit 4f7c297
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 12 deletions.
7 changes: 7 additions & 0 deletions app/assets/javascripts/main.js.coffee.erb
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ jQuery $ ->

if( $("#tweet").length > 0)
$("#tweet").change(recordTickyboxChange)

#########################################
# Delete account
#########################################
$("#delete_account").bind "click", (ev) ->
ev.preventDefault()
$(this).closest("form").submit()
23 changes: 23 additions & 0 deletions app/assets/stylesheets/content.scss.erb
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,29 @@
}
}

.delete-account {
.to_delete {
@include clearfix;
}
a {
float: right;
}
}

.js #delete .delete_account_submit {
display: none;
}

.no-js #delete a#delete_account {
display: none;
}

.destructive_action:hover {
background-color: #c88;
color: #fff;
border-color: #522;
}

/* ****************************** */
/* Sidebar */
/* ****************************** */
Expand Down
6 changes: 6 additions & 0 deletions app/assets/stylesheets/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ form {
}
}
}
input[type="text"] {
padding: 6px;
}
input.username{
width: 200px;
}

.form-submit {
@include prepend(4);
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/lib/css3buttons.scss.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@import "base";

.button { display: inline-block; padding: 7px 9px; font-size: 12px; line-height:1; color: #3C3C3D; text-shadow: 1px 1px 0 #FFFFFF; background: #ECECEC 0 0 no-repeat; white-space: nowrap; overflow: visible; cursor: pointer; text-decoration: none; border: 1px solid #CACACA; -webkit-border-radius: 2px; -moz-border-radius: 2px; -webkit-background-clip: padding-box; border-radius: 2px; outline: none; position: relative; zoom: 1; *display: inline; }
.button { @include copy-face; display: inline-block; padding: 7px 9px; font-size: 12px; color: #3C3C3D; text-shadow: 1px 1px 0 #FFFFFF; background: #ECECEC 0 0 no-repeat; white-space: nowrap; overflow: visible; cursor: pointer; text-decoration: none; border: 1px solid #CACACA; -webkit-border-radius: 2px; -moz-border-radius: 2px; -webkit-background-clip: padding-box; border-radius: 2px; outline: none; position: relative; zoom: 1; *display: inline; }
.button.primary { font-weight: bold }
.button:hover { color: #FFFFFF; border-color: #4c9092; text-decoration: none; text-shadow: -1px -1px 0 rgba(0,0,0,0.3); background-position: 0 -40px; background-color: $link_color; }
.button:active,
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ def show_layout?
!pjax_request?
end

def sign_in(user)
session[:user_id] = user.id
end

def sign_out
session[:user_id] = nil
@current_user = nil
end

private

MOBILE_BROWSERS = ["android", "ipod", "opera mini", "blackberry", "palm","hiptop","avantgo","plucker", "xiino","blazer","elaine", "windows ce; ppc;", "windows ce; smartphone;","windows ce; iemobile", "up.browser","up.link","mmp","symbian","smartphone", "midp","wap","vodafone","o2","pocket","kindle", "mobile","pda","psp","treo"]
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/auth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def auth
@auth.nickname = auth['info']['nickname']
@auth.save

session[:user_id] = @auth.user.id
sign_in(@auth.user)

flash[:notice] = "You're now logged in."

Expand Down Expand Up @@ -92,7 +92,7 @@ def destroy
auth = Authorization.first(:provider => params[:provider], :user_id => user.id)
auth.destroy if auth
# Without re-setting the session[:user_id] we're logged out
session[:user_id] = user.id
sign_in(user)
end

redirect_to edit_user_path(user)
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def create
if @user.valid?
if params[:password].length > 0
@user.save
session[:user_id] = @user.id
sign_in(@user)
flash[:notice] = "Thanks for signing up!"
redirect_to root_path
return
Expand All @@ -44,7 +44,7 @@ def create
render :new
else
if user = User.authenticate(params[:username], params[:password])
session[:user_id] = user.id
sign_in(user)
flash[:notice] = "Login successful."
redirect_to root_path
return
Expand All @@ -57,7 +57,7 @@ def create
end

def destroy
session[:user_id] = nil
sign_out
flash[:notice] = "You've been logged out."
redirect_to root_path
end
Expand Down
22 changes: 20 additions & 2 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class UsersController < ApplicationController
before_filter :find_user, :only => [:show, :edit, :update, :feed, :following, :followers]
before_filter :require_user, :only => [:edit, :update, :confirm_delete, :destroy]

def index
@title = "users"
Expand Down Expand Up @@ -98,7 +99,7 @@ def create
Authorization.create_from_session!(session, @user)

flash[:notice] = "Thanks! You're all signed up with #{@user.username} for your username."
session[:user_id] = @user.id
sign_in(@user)
redirect_to root_path
else
render :new
Expand Down Expand Up @@ -197,7 +198,7 @@ def confirm_email
user.email_confirmed = true
user.reset_perishable_token
# Register a session for the user
session[:user_id] = user.id
sign_in(user)
flash[:notice] = "Email successfully confirmed."
redirect_to root_path
end
Expand Down Expand Up @@ -311,6 +312,23 @@ def reset_password_with_token
end
end

def confirm_delete
end

def destroy
if current_user && params[:username_confirmation] == current_user.username
current_user.destroy
sign_out
flash[:notice] = "Your account has been deleted. We're sorry to see you go."
redirect_to root_path
elsif current_user
flash[:notice] = "Nothing was deleted since you did not type your username."
redirect_to edit_user_path
else
redirect_to root_path
end
end

private

def find_user
Expand Down
4 changes: 2 additions & 2 deletions app/models/author.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class Author
# Associations

# As we said, an Author has a Feed that they're the... author of. And if
# they're local, they also have a User, too.
one :feed
# they're local, they also have a User.
one :feed, :dependent => :destroy
one :user

# This takes results from an omniauth reponse and generates an author
Expand Down
2 changes: 1 addition & 1 deletion app/models/feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Feed
belongs_to :author
key :author_id, ObjectId

many :updates, :order => 'created_at desc'
many :updates, :order => 'created_at desc', :dependent => :destroy

timestamps!

Expand Down
9 changes: 9 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ class User
# This will establish other entities related to the User
after_create :finalize

# Mongo_mapper does not run :dependent => :destroy on belongs_to
# relationships, so clean up manually.
# https://github.com/jnunemaker/mongomapper/blob/master/test/functional/associations/test_belongs_to_proxy.rb#L155
before_destroy :clean_up

def clean_up
self.author.destroy
end

def feed
self.author.feed
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/static/follow.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#follow-form.search
= form_tag "/subscriptions" do
%input{:type => "text", :name => "subscribe_to"}
%input{:type => "text", :name => "subscribe_to", :class => "username" }
%input.button.follow{:type => "submit", :value => "Follow"}

%h3 OStatus Sites
Expand Down
23 changes: 23 additions & 0 deletions app/views/users/confirm_delete.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#delete
%h3 Warning

%p
You are about to delete your account. This will also delete all your status updates, and this cannot be undone.

%p
If you are sure you want to do this, please type your username as confirmation:

= form_tag user_path(current_user), :class => "delete_account_confirm" do
%input{:type => "hidden", :name => "_method", :value => "delete"}
%input{:type => :text, :name => :username_confirmation, :class => "username"}

-# We have both a submit button and links here because of a Firefox
-# CSS bug that sets line-height on buttons that can't be overridden.
-# Since Cancel should be a link that goes back to the edit profile
-# page, to match the height in Firefox, both Delete and Cancel are links.
-# Having the submit button, which is hidden using CSS, allows enter to
-# submit the form and makes something that will work without javascript.

%input{:type => "submit", :value => "Delete Account", :class => "delete_account_submit button"}
= link_to "Delete Account", "#", :id => "delete_account", :class => "button destructive_action"
= link_to "Cancel", edit_user_path(current_user), :class => "button"
6 changes: 6 additions & 0 deletions app/views/users/edit.haml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@
- else
%form.profile-update{:action => "/auth/twitter", :method => "GET", :name => "profile_update_form"}
%input.button{:type => "submit", :value => "Add Twitter Account"}

.delete-account.bottom-block
%h4 Delete Account

.to_delete
= link_to "Delete Account", account_deletion_confirmation_path, :class => "button destructive_action"
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
match 'reset_password', :to => "users#reset_password_new", :via => :get
match 'reset_password', :to => "users#reset_password_create", :via => :post
match 'reset_password/:token', :to => "users#reset_password_with_token", :via => :get, :as => "reset_password"
match 'users/:id/confirm_delete', :to => "users#confirm_delete", :constraints => { :id => /[^\/]+/ }, :as => "account_deletion_confirmation", :via => :get

# Updates
resources :updates, :only => [:index, :show, :create, :destroy]
Expand Down
6 changes: 6 additions & 0 deletions test/acceptance/acceptance_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,10 @@ def get_user_xrd user

Nokogiri.XML(last_response.body)
end

def logged_out?
within "#header" do
assert has_no_content?("Log Out")
end
end
end
72 changes: 72 additions & 0 deletions test/acceptance/account_deletion_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require 'require_relative' if RUBY_VERSION[0,3] == '1.8'
require_relative 'acceptance_helper'

describe "Delete your account" do
include AcceptanceHelper

before do
@u = Fabricate(:user)
@update = Fabricate(:update)
@u.feed.updates << @update
log_in_username(@u)
end

it "lets you delete your account and deletes all your updates" do
visit "/users/#{@u.username}/edit"
click_link "Delete Account"

fill_in "username_confirmation", :with => @u.username
click_button "Delete Account"

within flash do
assert has_content?("Your account has been deleted. We're sorry to see you go.")
end

assert logged_out?

visit "/updates"
within "#updates" do
assert has_no_content?(@update.text)
end
end

it "returns you to your edit page if you click cancel" do
visit "/users/#{@u.username}/edit"
click_link "Delete Account"
click_link "Cancel"
page.current_url.must_match("/users/#{@u.username}/edit")
end

it "returns you to your edit page if you dont type a username" do
visit "/users/#{@u.username}/edit"
click_link "Delete Account"
click_button "Delete Account"

page.current_url.must_match("/users/#{@u.username}/edit")
within flash do
assert has_content?("Nothing was deleted since you did not type your username.")
end
end

it "returns you to your edit page if you type your username wrong" do
visit "/users/#{@u.username}/edit"
click_link "Delete Account"
fill_in "username_confirmation", :with => "nopenopenope"
click_button "Delete Account"

page.current_url.must_match("/users/#{@u.username}/edit")
within flash do
assert has_content?("Nothing was deleted since you did not type your username.")
end
end

it "does not let you delete someone else's account" do
@someone_else = Fabricate(:user, :username => "someone_else")
delete "/users/someone_else"

visit "/users/someone_else"
within "span.user-text" do
assert has_content?("someone_else")
end
end
end
7 changes: 7 additions & 0 deletions test/acceptance/profile_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,14 @@
end
end

it "doesn't let a logged out user update someone's profile" do
u = Fabricate(:user)
visit "/users/#{u.username}/edit"
page.current_url.wont_match(/\/users\/#{u.username}\/edit/)
end

it "doesn't let you update someone else's profile" do
log_in_as_some_user
u = Fabricate(:user)
visit "/users/#{u.username}/edit"
assert_match /\/users\/#{u.username}$/, page.current_url
Expand Down

0 comments on commit 4f7c297

Please sign in to comment.