Now I realise this topic has been covered many times before. However there did not appear to be a solution that wanted to make the current password field exempt only when the pa
Okay so I have finally figured this out!
Add the following to your class RegistrationsController < Devise::RegistrationsController
def update
if resource.update_with_password(params[resource_name])
set_flash_message :notice, :updated
sign_in resource_name, resource, :bypass => true
redirect_to after_update_path_for(resource)
else
clean_up_passwords(resource)
render_with_scope :edit
end
end
Then the following to your User model:
def update_with_password(params={})
current_password = params.delete(:current_password) if !params[:current_password].blank?
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if has_no_password? || valid_password?(current_password)
update_attributes(params)
else
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
self.attributes = params
false
end
clean_up_passwords
result
end
def has_no_password?
self.encrypted_password.blank?
end
The only thing I was slightly confused about is that in the edit view:
<% if !current_user.has_no_password? %>
I wrapped it in that if, I would have thought it would have been:
<% if current_user.has_no_password? %>
If anyone can see anything to improve my code or a different, more efficient way let me know!
Another very minor change on requiring the current password field or not: my goal is never require a current password unless they are updating their password. for oauth'ed accounts, I check for the facebook id in the view, and don't display the password fields at all.
In the registrations controller:
Users::RegistrationsController < Devise::RegistrationsController
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
if resource.update_with_password(params[resource_name])
set_flash_message :notice, :updated if is_navigational_format?
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords(resource)
respond_with_navigational(resource){ render_with_scope :edit }
end
end
In User.rb, I use update_with_password, and then call verify_password_and_update only if the user has entered something in the password field from the view. Otherwise, I clear the current password param and call update_without_password (this method is built in to devise now as well):
#to remove the current password check if updating a profile originally gotten via oauth (fb, twitter)
def update_with_password(params={})
if params[:password].blank?
params.delete(:current_password)
self.update_without_password(params)
else
self.verify_password_and_update(params)
end
end
def update_without_password(params={})
params.delete(:password)
params.delete(:password_confirmation)
result = update_attributes(params)
clean_up_passwords
result
end
def verify_password_and_update(params)
#devises' update_with_password
# https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb
current_password = params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if valid_password?(current_password)
update_attributes(params)
else
self.attributes = params
self.valid?
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
clean_up_passwords
result
end