Merge remote branch 'yolk/348-change-email-with-confirmation' into merge_email_change
Conflicts: app/controllers/users_controller.rb config/locales/diaspora/en.yml db/schema.rb spec/controllers/users_controller_spec.rb spec/mailers/notifier_spec.rb
This commit is contained in:
commit
9797e04957
15 changed files with 355 additions and 4 deletions
|
|
@ -48,6 +48,14 @@ class UsersController < ApplicationController
|
|||
else
|
||||
flash[:error] = I18n.t 'users.update.language_not_changed'
|
||||
end
|
||||
elsif u[:email]
|
||||
@user.unconfirmed_email = u[:email]
|
||||
if @user.save
|
||||
@user.mail_confirm_email
|
||||
flash[:notice] = I18n.t 'users.update.unconfirmed_email_changed'
|
||||
else
|
||||
flash[:error] = I18n.t 'users.update.unconfirmed_email_not_changed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -126,4 +134,13 @@ class UsersController < ApplicationController
|
|||
tar_path = PhotoMover::move_photos(current_user)
|
||||
send_data( File.open(tar_path).read, :filename => "#{current_user.id}.tar" )
|
||||
end
|
||||
|
||||
def confirm_email
|
||||
if current_user.confirm_email(params[:token])
|
||||
flash[:notice] = I18n.t('users.confirm_email.email_confirmed', :email => current_user.email)
|
||||
elsif current_user.unconfirmed_email.present?
|
||||
flash[:error] = I18n.t('users.confirm_email.email_not_confirmed')
|
||||
end
|
||||
redirect_to edit_user_path
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -113,6 +113,16 @@ class Notifier < ActionMailer::Base
|
|||
end
|
||||
end
|
||||
|
||||
def confirm_email(receiver_id)
|
||||
@receiver = User.find_by_id(receiver_id)
|
||||
|
||||
I18n.with_locale(@receiver.language) do
|
||||
mail(:to => "\"#{@receiver.name}\" <#{@receiver.unconfirmed_email}>",
|
||||
:subject => I18n.t('notifier.confirm_email.subject', :unconfirmed_email => @receiver.unconfirmed_email),
|
||||
:host => AppConfig[:pod_uri].host)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def log_mail recipient_id, sender_id, type
|
||||
log_string = "event=mail mail_type=#{type} recipient_id=#{recipient_id} sender_id=#{sender_id}"
|
||||
|
|
|
|||
8
app/models/jobs/mail_confirm_email.rb
Normal file
8
app/models/jobs/mail_confirm_email.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module Job
|
||||
class MailConfirmEmail < Base
|
||||
@queue = :mail
|
||||
def self.perform_delegate(user_id)
|
||||
Notifier.confirm_email(user_id).deliver
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -24,6 +24,7 @@ class User < ActiveRecord::Base
|
|||
validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/
|
||||
validates_length_of :username, :maximum => 32
|
||||
validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES
|
||||
validates_format_of :unconfirmed_email, :with => Devise.email_regexp, :allow_blank => true
|
||||
|
||||
validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?}
|
||||
validates_associated :person
|
||||
|
|
@ -48,6 +49,7 @@ class User < ActiveRecord::Base
|
|||
before_save do
|
||||
person.save if person && person.changed?
|
||||
end
|
||||
before_save :guard_unconfirmed_email
|
||||
|
||||
attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail
|
||||
|
||||
|
|
@ -101,6 +103,12 @@ class User < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
|
||||
def confirm_email(token)
|
||||
return false if token.blank? || token != confirm_email_token
|
||||
self.email = unconfirmed_email
|
||||
save
|
||||
end
|
||||
|
||||
######### Aspects ######################
|
||||
|
||||
def move_contact(person, to_aspect, from_aspect)
|
||||
|
|
@ -214,6 +222,12 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def mail_confirm_email
|
||||
return false if unconfirmed_email.blank?
|
||||
Resque.enqueue(Job::MailConfirmEmail, id)
|
||||
true
|
||||
end
|
||||
|
||||
######### Posts and Such ###############
|
||||
def retract(target)
|
||||
if target.respond_to?(:relayable?) && target.relayable?
|
||||
|
|
@ -365,4 +379,12 @@ class User < ActiveRecord::Base
|
|||
def remove_mentions
|
||||
Mention.where( :person_id => self.person.id).delete_all
|
||||
end
|
||||
|
||||
def guard_unconfirmed_email
|
||||
self.unconfirmed_email = nil if unconfirmed_email.blank? || unconfirmed_email == email
|
||||
|
||||
if unconfirmed_email_changed?
|
||||
self.confirm_email_token = unconfirmed_email ? ActiveSupport::SecureRandom.hex(15) : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
6
app/views/notifier/confirm_email.html.haml
Normal file
6
app/views/notifier/confirm_email.html.haml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
%p
|
||||
= t('notifier.hello', :name => @receiver.profile.first_name)
|
||||
%p
|
||||
!= t('notifier.confirm_email.click_link', :unconfirmed_email => @receiver.unconfirmed_email)
|
||||
%br
|
||||
= link_to confirm_email_url(:token => @receiver.confirm_email_token), confirm_email_url(:token => @receiver.confirm_email_token)
|
||||
4
app/views/notifier/confirm_email.text.haml
Normal file
4
app/views/notifier/confirm_email.text.haml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
!= t('notifier.hello', :name => @receiver.profile.first_name)
|
||||
|
||||
!= t('notifier.confirm_email.click_link', :unconfirmed_email => @receiver.unconfirmed_email)
|
||||
!= confirm_email_url(:token => @receiver.confirm_email_token)
|
||||
|
|
@ -19,8 +19,15 @@
|
|||
.span-5.last
|
||||
%h3
|
||||
= t('.your_email')
|
||||
%p
|
||||
= current_user.email
|
||||
= form_for 'user', :url => user_path, :html => { :method => :put } do |f|
|
||||
= f.error_messages
|
||||
%p
|
||||
= f.text_field :email, :value => @user.unconfirmed_email || @user.email
|
||||
= f.submit t('.change_email')
|
||||
%br
|
||||
- if @user.unconfirmed_email.present?
|
||||
%p= t('.email_awaiting_confirmation', :email => @user.email, :unconfirmed_email => @user.unconfirmed_email)
|
||||
%br
|
||||
|
||||
%br
|
||||
%br
|
||||
|
|
|
|||
|
|
@ -420,7 +420,9 @@ en:
|
|||
liked:
|
||||
liked: "%{name} just liked your post"
|
||||
view_post: "View post >"
|
||||
|
||||
confirm_email:
|
||||
subject: "Please activate your new e-mail address %{unconfirmed_email}"
|
||||
click_link: "To activate your new e-mail address %{unconfirmed_email}, please click this link:"
|
||||
people:
|
||||
zero: "no people"
|
||||
one: "1 person"
|
||||
|
|
@ -723,6 +725,7 @@ en:
|
|||
close_account: "Close Account"
|
||||
change_language: "Change Language"
|
||||
change_password: "Change Password"
|
||||
change_email: "Change E-Mail"
|
||||
new_password: "New Password"
|
||||
current_password: "Current password"
|
||||
download_xml: "download my xml"
|
||||
|
|
@ -738,6 +741,7 @@ en:
|
|||
private_message: "...you receive a private message?"
|
||||
liked: "...someone likes your post?"
|
||||
change: "Change"
|
||||
email_awaiting_confirmation: "We have sent you an activation link to %{unconfirmed_email}. Till you follow this link and activate the new address, we will continue to use your original address %{email}."
|
||||
destroy: "Your account has been locked. It may take 20 minutes for us to finish closing your account. Thank you for trying Diaspora."
|
||||
getting_started:
|
||||
welcome: "Welcome to Diaspora!"
|
||||
|
|
@ -761,8 +765,13 @@ en:
|
|||
language_changed: "Language Changed"
|
||||
language_not_changed: "Language Change Failed"
|
||||
email_notifications_changed: "Email notifications changed"
|
||||
unconfirmed_email_changed: "E-Mail Changed. Needs activation."
|
||||
unconfirmed_email_not_changed: "E-Mail Change Failed"
|
||||
public:
|
||||
does_not_exist: "User %{username} does not exist!"
|
||||
confirm_email:
|
||||
email_confirmed: "E-Mail %{email} activated"
|
||||
email_not_confirmed: "E-Mail could not be activated. Wrong link?"
|
||||
|
||||
webfinger:
|
||||
fetch_failed: "failed to fetch webfinger profile for %{profile_url}"
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ Diaspora::Application.routes.draw do
|
|||
get 'public/:username' => :public, :as => 'users_public'
|
||||
match 'getting_started' => :getting_started, :as => 'getting_started'
|
||||
get 'getting_started_completed' => :getting_started_completed
|
||||
get 'confirm_email/:token' => :confirm_email, :as => 'confirm_email'
|
||||
end
|
||||
|
||||
# This is a hack to overide a route created by devise.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
class AddUnconfirmedEmailToUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :users, :unconfirmed_email, :string, :default => nil, :null => true
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :users, :unconfirmed_email
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
class AddConfirmEmailTokenToUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :users, :confirm_email_token, :string, :limit => 30
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :users, :confirm_email_token
|
||||
end
|
||||
end
|
||||
|
|
@ -391,6 +391,8 @@ ActiveRecord::Schema.define(:version => 20110707234802) do
|
|||
t.integer "invited_by_id"
|
||||
t.string "invited_by_type"
|
||||
t.string "authentication_token", :limit => 30
|
||||
t.string "unconfirmed_email"
|
||||
t.string "confirm_email_token", :limit => 30
|
||||
t.datetime "locked_at"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,41 @@ describe UsersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'email' do
|
||||
before do
|
||||
Resque.stub!(:enqueue)
|
||||
end
|
||||
|
||||
it 'allow the user to change his (unconfirmed) email' do
|
||||
put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
|
||||
@user.reload
|
||||
@user.unconfirmed_email.should eql("my@newemail.com")
|
||||
end
|
||||
|
||||
it 'informs the user about success' do
|
||||
put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
|
||||
request.flash[:notice].should eql(I18n.t('users.update.unconfirmed_email_changed'))
|
||||
request.flash[:error].should be_blank
|
||||
end
|
||||
|
||||
it 'informs the user about failure' do
|
||||
put(:update, :id => @user.id, :user => { :email => "my@newemailcom"})
|
||||
request.flash[:error].should eql(I18n.t('users.update.unconfirmed_email_not_changed'))
|
||||
request.flash[:notice].should be_blank
|
||||
end
|
||||
|
||||
it 'allow the user to change his (unconfirmed) email to blank (= abort confirmation)' do
|
||||
put(:update, :id => @user.id, :user => { :email => ""})
|
||||
@user.reload
|
||||
@user.unconfirmed_email.should eql(nil)
|
||||
end
|
||||
|
||||
it 'sends out activation email on success' do
|
||||
Resque.should_receive(:enqueue).with(Job::MailConfirmEmail, @user.id).once
|
||||
put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email settings' do
|
||||
it 'lets the user turn off mail' do
|
||||
par = {:id => @user.id, :user => {:email_preferences => {'mentioned' => 'true'}}}
|
||||
|
|
@ -138,4 +173,31 @@ describe UsersController do
|
|||
alice.reload.access_locked?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#confirm_email' do
|
||||
before do
|
||||
@user.update_attribute(:unconfirmed_email, 'my@newemail.com')
|
||||
end
|
||||
|
||||
it 'redirects to to the user edit page' do
|
||||
get 'confirm_email', :token => @user.confirm_email_token
|
||||
response.should redirect_to edit_user_path
|
||||
end
|
||||
|
||||
it 'confirms email' do
|
||||
get 'confirm_email', :token => @user.confirm_email_token
|
||||
@user.reload
|
||||
@user.email.should eql('my@newemail.com')
|
||||
request.flash[:notice].should eql(I18n.t('users.confirm_email.email_confirmed', :email => 'my@newemail.com'))
|
||||
request.flash[:error].should be_blank
|
||||
end
|
||||
|
||||
it 'does NOT confirm email with wrong token' do
|
||||
get 'confirm_email', :token => @user.confirm_email_token.reverse
|
||||
@user.reload
|
||||
@user.email.should_not eql('my@newemail.com')
|
||||
request.flash[:error].should eql(I18n.t('users.confirm_email.email_not_confirmed'))
|
||||
request.flash[:notice].should be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe Notifier do
|
|||
end
|
||||
|
||||
it 'has the layout' do
|
||||
|
||||
|
||||
mail = Notifier.single_admin("Welcome to bureaucracy!", user)
|
||||
mail.body.encoded.should match /change your notification settings/
|
||||
end
|
||||
|
|
@ -217,5 +217,33 @@ describe Notifier do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".confirm_email" do
|
||||
before do
|
||||
user.update_attribute(:unconfirmed_email, "my@newemail.com")
|
||||
end
|
||||
|
||||
let!(:confirm_email) { Notifier.confirm_email(user.id) }
|
||||
|
||||
it 'goes to the right person' do
|
||||
confirm_email.to.should == [user.unconfirmed_email]
|
||||
end
|
||||
|
||||
it 'has the unconfirmed emil in the subject' do
|
||||
confirm_email.subject.should include(user.unconfirmed_email)
|
||||
end
|
||||
|
||||
it 'has the unconfirmed emil in the body' do
|
||||
confirm_email.body.encoded.should include(user.unconfirmed_email)
|
||||
end
|
||||
|
||||
it 'has the receivers name in the body' do
|
||||
confirm_email.body.encoded.should include(user.person.profile.first_name)
|
||||
end
|
||||
|
||||
it 'has the activation link in the body' do
|
||||
confirm_email.body.encoded.should include(confirm_email_url(:token => user.confirm_email_token))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -114,6 +114,31 @@ describe User do
|
|||
alice.email = eve.email
|
||||
alice.should_not be_valid
|
||||
end
|
||||
|
||||
it "requires a vaild email address" do
|
||||
alice.email = "somebody@anywhere"
|
||||
alice.should_not be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "of unconfirmed_email" do
|
||||
it "unconfirmed_email address can be nil/blank" do
|
||||
alice.unconfirmed_email = nil
|
||||
alice.should be_valid
|
||||
alice.unconfirmed_email = ""
|
||||
alice.should be_valid
|
||||
end
|
||||
|
||||
it "does NOT require a unique unconfirmed_email address" do
|
||||
eve.update_attribute :unconfirmed_email, "new@email.com"
|
||||
alice.unconfirmed_email = "new@email.com"
|
||||
alice.should be_valid
|
||||
end
|
||||
|
||||
it "requires a vaild unconfirmed_email address" do
|
||||
alice.unconfirmed_email = "somebody@anywhere"
|
||||
alice.should_not be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "of language" do
|
||||
|
|
@ -595,4 +620,136 @@ describe User do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'change email' do
|
||||
let(:user){ alice }
|
||||
|
||||
describe "#unconfirmed_email" do
|
||||
it "is nil by default" do
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
end
|
||||
|
||||
it "forces blank to nil" do
|
||||
user.unconfirmed_email = ""
|
||||
user.save!
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
end
|
||||
|
||||
it "is ignored if it equals email" do
|
||||
user.unconfirmed_email = user.email
|
||||
user.save!
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
end
|
||||
|
||||
it "allows change to valid new email" do
|
||||
user.unconfirmed_email = "alice@newmail.com"
|
||||
user.save!
|
||||
user.unconfirmed_email.should eql("alice@newmail.com")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#confirm_email_token" do
|
||||
it "is nil by default" do
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
|
||||
it "is autofilled when unconfirmed_email is set to new email" do
|
||||
user.unconfirmed_email = "alice@newmail.com"
|
||||
user.save!
|
||||
user.confirm_email_token.should_not be_blank
|
||||
user.confirm_email_token.size.should eql(30)
|
||||
end
|
||||
|
||||
it "is set back to nil when unconfirmed_email is empty" do
|
||||
user.unconfirmed_email = "alice@newmail.com"
|
||||
user.save!
|
||||
user.confirm_email_token.should_not be_blank
|
||||
user.unconfirmed_email = nil
|
||||
user.save!
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
|
||||
it "generates new token on every new unconfirmed_email" do
|
||||
user.unconfirmed_email = "alice@newmail.com"
|
||||
user.save!
|
||||
first_token = user.confirm_email_token
|
||||
user.unconfirmed_email = "alice@andanotherone.com"
|
||||
user.save!
|
||||
user.confirm_email_token.should_not eql(first_token)
|
||||
user.confirm_email_token.size.should eql(30)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#mail_confirm_email' do
|
||||
it 'enqueues a mail job on user with unconfirmed email' do
|
||||
user.update_attribute(:unconfirmed_email, "alice@newmail.com")
|
||||
Resque.should_receive(:enqueue).with(Job::MailConfirmEmail, alice.id).once
|
||||
alice.mail_confirm_email.should eql(true)
|
||||
end
|
||||
|
||||
it 'enqueues NO mail job on user without unconfirmed email' do
|
||||
Resque.should_not_receive(:enqueue).with(Job::MailConfirmEmail, alice.id)
|
||||
alice.mail_confirm_email.should eql(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#confirm_email' do
|
||||
context 'on user with unconfirmed email' do
|
||||
before do
|
||||
user.update_attribute(:unconfirmed_email, "alice@newmail.com")
|
||||
end
|
||||
|
||||
it 'confirms email and set the unconfirmed_email to email on valid token' do
|
||||
user.confirm_email(user.confirm_email_token).should eql(true)
|
||||
user.email.should eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
|
||||
it 'returns false and does not change anything on wrong token' do
|
||||
user.confirm_email(user.confirm_email_token.reverse).should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should_not eql(nil)
|
||||
user.confirm_email_token.should_not eql(nil)
|
||||
end
|
||||
|
||||
it 'returns false and does not change anything on blank token' do
|
||||
user.confirm_email("").should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should_not eql(nil)
|
||||
user.confirm_email_token.should_not eql(nil)
|
||||
end
|
||||
|
||||
it 'returns false and does not change anything on blank token' do
|
||||
user.confirm_email(nil).should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should_not eql(nil)
|
||||
user.confirm_email_token.should_not eql(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on user without unconfirmed email' do
|
||||
it 'returns false and does not change anything on any token' do
|
||||
user.confirm_email("12345"*6).should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
|
||||
it 'returns false and does not change anything on blank token' do
|
||||
user.confirm_email("").should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
|
||||
it 'returns false and does not change anything on blank token' do
|
||||
user.confirm_email(nil).should eql(false)
|
||||
user.email.should_not eql("alice@newmail.com")
|
||||
user.unconfirmed_email.should eql(nil)
|
||||
user.confirm_email_token.should eql(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue