Using Devise with Omniauth I was successfully able to allow users to login with their Facebook accounts. With the help of the Omniauth wiki I was also able to disable having to put in a password when editing their account.
The current problem I'm faced with is hiding the password fields in my registrations#edit
view if they signed up via Facebook. As a newcomer to both gems I'm still trying to adjust. So when it comes to setting up the necessary helper method(s) I'm sort've at a loss.
As instructed by the wiki here's what my Registrations controller looks like:
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters, if: :devise_controller?
def update
@user = User.find(current_user.id)
successfully_updated = if needs_password?(@user, params)
@user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
else
# remove the virtual current_password attribute
# update_without_password doesn't know how to ignore it
params[:user].delete(:current_password)
@user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
end
if successfully_updated
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case their password changed
sign_in @user, :bypass => true
redirect_to after_update_path_for(@user)
else
render "edit"
end
end
private
# check if we need password to update user data
# ie if password or email was changed
# extend this as needed
def needs_password?(user, params)
user.email != params[:user][:email] ||
params[:user][:password].present? ||
params[:user][:password_confirmation].present?
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
end
And here is my registrations#edit
view:
<% provide :title, 'Edit Profile' %>
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :first_name %><br />
<%= f.text_field :first_name %></div>
<div><%= f.label :last_name %><br />
<%= f.text_field :last_name %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off" %></div>
<div><%= f.submit "Update" %></div>
<% end %>
UPDATE
After some extensive research I was able to find a question that inderictly answered what I was looking for in terms of hiding the input fields (second to last comment in the question). The only problem is that for some reason it triggers a password validation error despite it not being there...
<div>
<%= f.label :first_name %><br />
<%= f.text_field :first_name %>
</div>
<div>
<%= f.label :last_name %><br />
<%= f.text_field :last_name %>
</div>
<% if user_signed_in? && !current_user.provider == 'facebook' %>
<div>
<%= f.label :email %><br />
<%= f.email_field :email %>
</div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div>
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div>
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
</div>
After searching through the site I have FINALLY found out how to get this to work.
My first order of business was to to use the first code example for the registrations controller from this devise wiki
def update
# For Rails 4
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# For Rails 3
# account_update_params = params[:user]
# required for settings form to submit when password is left blank
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
@user = User.find(current_user.id)
if @user.update_attributes(account_update_params)
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case their password changed
sign_in @user, :bypass => true
redirect_to after_update_path_for(@user)
else
render "edit"
end
end
Next was adding a method to override the validation for the user's current password just above the code above. Special thanks to Lauri Laine for her answer in this post: Editing Users With Devise and Omniauth
# Overwrite update_resource to let users to update their user without giving their password
def update_resource(resource, params)
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(params)
else
resource.update_with_password(params)
end
end
Lastly I had to add :current_password
as a virtual attribute to my user model and everything worked!
attr_accessor :current_password
Hopefully this helps anyone who has/will encounter this problem. I know it had me stumped for a minute but glad I was able to figure it all out. Will be testing this soon in conjunction with omniauth's twitter gem.