I'm new to Devise and I'm trying to update user password for the users who signed up through normal sign up(form) and via Omniauth(facebook,google_oauth2).
Below is my code.
Edit form:
#app/views/devise/registrations/edit.html.erb
<h2>Change Your Details</h2>
<div><b><i>View or update your Current Email and Password <%= link_to 'Click Here', '#', :id => 'email_change' %></b></i></div>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div style="color:red">Currently waiting confirmation for: <%= resource.unconfirmed_email %>. Please confirm the new email address before using it for sign in.</div>
<% end %>
<div id='display_email'><%= f.label :current_email %> <i>(this email is currently used for email alerts and sign in)</i><br />
<%= f.email_field :email, autocomplete: "off", :class => 'form-control' %> <i>(edit this field to change your email)</i></div>
<% if resource.provider.blank? %>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off", :class => 'form-control' %></div>
<% end %>
<div><%= f.label :new_password %> <i></i><br />
<%= f.password_field :password, autocomplete: "off", :class => 'form-control' %></div>
<div><%= f.label :new_password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off", :class => 'form-control' %></div>
<br>
<div><%= f.submit "Update", :class => 'btn btn-lg btn-primary btn-block' %></div>
<% end %>
Update method:
#app/controllers/registrations_controller.rb
def update
# For Rails 4
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# delete field if it is blank, so it does not update
permitted_update_fields_hash = [:first_name, :last_name, :email, :primary_phone, :password, :password_confirmation]
permitted_update_fields = [:first_name, :last_name, :email, :primary_phone, :password, :password_confirmation]
puf = permitted_update_fields.length - 1
index = 0
while index <= puf
if account_update_params[permitted_update_fields_hash[index]].blank?
account_update_params.delete(permitted_update_fields[index])
end
index += 1
end
# 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
if #user.role == "renter"
redirect_to renter_dashboard_path(resource.renter.slug)
else
redirect_to request.referer
end
else
redirect_to request.referer
end
end
Problem:
Users who signed up through facebook,google_oauth2 can't able to provide current password because they actually don't know the current password. This is because Devise generates a random password on sign up
user.password = Devise.friendly_token[0,20]
Current Solution:
Currently I'm hiding the current_password field for the users who signed up through facebook & google_oauth2(see the below image)
Question:
Is there any better alternative approach for this situation?
Related
This is application Controller for devise
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
end
end
signup form from devise and adding role
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
This is a role code
There is a problem when user select buyer on signup page it should go the homepage , when user select seller it should go to the selled dashboard
<%= f.label :role %>
<%= f.select :role, User.roles.keys %>
Here is the submit button
<div class="actions">
<%= f.submit "Sign up", data: {turbo: false} %>
</div>
<% end %>
<%= render "devise/shared/links" %>
You didn't provide much context about what you do in your controller when a user is signed up and how you build the form. Therefore I will just assume that it is a very basic form and all attributes are nested properly in the request and that the create action follows a simple scaffolding structure.
Then you basically only need to change where to redirect the user to depending on its role after the record was created.
# in controllers/users.rb
def create
user = User.new(user_params)
if user.save
case user.role
when 'user'
redirect_to root_path
when 'seller'
redirect_to dashboard_path
end
else
render :edit
end
end
I've seen a lot of posts about how to map 2 form fields to one model field, but what about mapping 1 form field to provide answers for two (or more) model fields? I have a form for users which gives a field, last_name, for a user. But I want the default password for that user to also be last_name (and I have password_confirmation set up, so that also needs to be last_name). How would I do this?
Form:
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: 'form-control' %>
<%= f.label :last_name %>
<%= f.text_field :last_name, :password, :password_confirmation,
class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.hidden_field :access_level, :value => "Chair" %>
<%= f.label :Phone %>
<%= f.text_field :phone_number, :id => "phone", class: 'form-control' %>
<p> </p>
<div class="col-md-12">
<%= f.submit "Add Chair", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
user_controller.rb
def create
#user = User.new(user_params)
if #user.save
log_in #user
current_user
flash[:success] = "Welcome to the Penn Model Congress!"
redirect_to after_sign_in_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password,
:password_confirmation, :access_level,
:phone_number)
end
end
I would add a before_validation callback to the User model like this:
# in app/models/user.rb
before_validation :set_default_password
private
def set_default_password
self.password ||= last_name
self.password_confirmation ||= last_name
end
If this will be your default and you don't want the user to be able to override it on creation, you could set it on the controller:
View:
<%= f.text_field :last_name, class: 'form-control' %>
Controller:
def create
#user = User.new(user_params)
#user.password = #user.last_name
#user.password_confirmation = #user.last_name
if #user.save
log_in #user
current_user
flash[:success] = "Welcome to the Penn Model Congress!"
redirect_to after_sign_in_path
else
render 'new'
end
end
It's been a nightmare trying to integrate the two.
Update:
UsersController.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :confirm_password
def show
render_wizard
end
def update
render_wizard
end
end
RegistrationsController (devise)
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
end
protected
def users_steps_path(resource)
'/user_steps'
end
end
confirm_password.html.erb (view)
<%= form_for #user, url: wizard_path do |f| %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Change Password" %>
<% end %>
routes.rb
resources :user_steps
registrations/new.html.erb (sign up)
<div class="styled email-input2">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.email_field :email, autofocus: true, placeholder: "Email", class: "email-input" %></div>
<div><%= f.text_field :username, autofocus: true, placeholder: "Username", class: "email-input" %></div>
<div><%= f.password_field :password, autocomplete: "off", placeholder: "Password", class: "email-input" %></div>
<div><%= f.password_field :password_confirmation, autocomplete: "off", placeholder: "Password confirmation", class: "email-input" %></div>
</div>
<div class="get_motivated2">
<%= f.submit "Sign up", class: "get_motivated btn-danger" %>
<% end %>
</div>
</div>
This is what I have so far, but when I sign-up with devise it just logs me in.. It doesn't go to the :confirm_password step. Not sure what I'm doing wrong here, I've followed Ryan Bates tutorial on railscasts, but he doesn't use Devise which threw me off a bit.
I assume what you want to do is: after a user fills out the sign up form (a user record is not yet created in this step), the user is redirected to a page where she confirms her password one more time before her user account is created.
When you submit your signup form to registration_path(resource_name), devise will create a new user record and log you in. The place where you should submit all user information to create an user account is in confirm_password.html.erb. Submitting the sign up form should simply store the data (this is done by wicked) and redirect the user to the password confirmation page.
Hope this helps.
I'm new to Rails.
I have a register page in my app. Also a profile page. I'm trying to make an edit page where I can edit users email, password and all. I want to do all this using devise..
I have reached so far. here is my edit page.
<div class="edit_profile_page">
<%= form_for(current_user, :url => '/update', :html => { :method => :put }) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<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 %></div>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %></div>
<div><%= f.submit "Update" %></div>
<% end %>
</div>
I'm stuck here. What url should i pass.. Also if this url points to a method say
def update_profile
end
what should i write inside that method so that the password will get updated like the one happened while registration.
Or
There is an edit page inside Device. How should i write my routes to reach there.
You can also create own ProfilesController, example below:
Routes:
#routes.rb
resource :profile
Controller:
# coding: utf-8
class ProfilesController < ApplicationController
before_filter :authenticate_user!
def show
#user=current_user
#user.email = nil unless #user.email.scan('#example.com').empty?
render 'devise/profile/edit'
end
def update
#user=current_user
if #user.update_attributes(params[:user])
sign_in 'user', #user, :bypass => true
flash[:notice] = t('users.profile.edit.updated')
respond_to do |format|
format.html { redirect_to '/'}
end
else
render 'devise/profile/edit'
end
end
end
Views
#views/devise/profile/edit.html.haml
%h3
= t('users.profile.basic_settings')
= simple_form_for #user, :url => profile_path, :html => { :method => :put } do |f|
-#= f.error_messages
= f.input :name, :placeholder=>t('activerecord.placeholders.name')
= f.input :email, :placeholder=>t('activerecord.placeholders.email')
= f.submit t('users.profile.change_name'), :class => "btn btn-primary"
= t('users.profile.change_password')
= simple_form_for #user, :url => profile_path, :html => { :method => :put } do |f|
-#= f.error_messages
= f.input :password , :error_html => { :id => "password_error"}
= f.input :password_confirmation
= f.submit t('users.profile.change_password'), :class => "btn btn-primary"
Right now every time I change anything in user/edit the form requires the user to set a new password. I would like for it to require the current password (how can I ask for current password?) only incase a new password is entered. How can I achieve this, thanks a lot for this.
<%= form_for(#user, :html => {:multipart => true}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_field :name, placeholder: :name %>
<%= f.text_field :email, placeholder: :email %>
<%= f.password_field :password, placeholder: "Enter new password" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm new password" %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
Using some info from rails_has_elegance and the web I came up with the following solution.
user/edit view:
<%= form_for(#user, :html => {:multipart => true}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_field :name, placeholder: :name %>
<%= f.text_field :email, placeholder: :email %>
<%= password_field_tag :current_password, params[:current_password], placeholder: "Current password" %>
<%= f.password_field :password, placeholder: "New password (optional)" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm new password" %>
<% end %>
User model:
validates :password, :on => :create
validates :password_confirmation, presence: true, :on => :update, :unless => lambda{ |user| user.password.blank? }
User controller:
def update
#user = User.find(params[:id])
user = User.find_by_email(current_user.email).try(:authenticate, params[:current_password])
if user && #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
flash.now[:error] = "Incorrect Current Password" unless user
sign_in #user
render 'edit'
end
end
You can add in your form old_password field
<%= f.password_field :old_password, placeholder: "Enter current password" %>
Add it to attr_accessible :old_password and attr_accessor :old_password
And then you can validate it
validate :correct_old_pass, :on => :update
def correct_old_pass
errors[:old_password] << 'Incorrect pass' if your_check_method
end
You can create a separate form for changing password. And you can ask for current password just like you ask for a new one:
<%= form_for(#user, :html => {:multipart => true}) do |f| %>
<%= f.password_field :password, placeholder: "Enter current password" %>
<%= f.password_field :password, placeholder: "Enter new password" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm new password" %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
I would suggest making extra edit_password and update_password actions for that in your users_controller:
#user = User.find(params[:id])
user = User.find_by_email(current_user.email).try(:authenticate, params[:current_password])
if user && #user.update_attributes(params[:user])
flash[:success] = "Password updated!"
(sign_in #user)
redirect_to ...
In your form just use these fields:
<%= label_tag :current_password, "Current password:" %>
<%= password_field_tag :current_password, params[:current_password] %>
<%= form.label :password, "New password:" %>
<%= form.password_field :password %>
<%= form.label :password_confirmation, "Confirm new password" %>
<%= form.password_field :password_confirmation %>