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"
Related
I am trying to add a solution to allow users to change their password. I am using devise. And I followed up the third solution from this tutorial.
However after I do everything:
class UsersController < ApplicationController
before_action :authenticate_user!
def edit
#user = current_user
end
def update_password
#user = current_user
if #user.update_with_password(user_params)
# Sign in the user by passing validation in case their password changed
bypass_sign_in(#user)
redirect_to root_path
else
render "edit"
end
end
private
def user_params
# NOTE: Using `strong_parameters` gem
params.require(:user).permit(:password, :password_confirmation, :current_password)
end
end
in routes.rb
resource :user, only: [:edit] do
collection do
patch 'update_password'
end
end
and in the view:
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
</div>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
<% end %>
but I get this error First argument in form cannot contain nil or be empty in
= form_for(#user, :url => { :action => 'update_password' } ) do |f|
is there something I have missed? Thanks!
I am using carrierwave and minimagick gem to save user for picture uploads in Rails. I have added picture field to the the devise forms and updated the User model to include the mountuploader file.
However, when I try to save, the users attributes are all saved and updated in the database except for the picture.
When I check the User in the console, picture attribute is nil . Any ideas ? My other models save the picture just fine.
Here are my devise forms:
devise_registerations/new
<div class="authform">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form', multipart: true}) do |f| %>
<h3>Sign up</h3>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :country %>
<%= country_select(resource_name, "country")%>
</div>
<div class="form-group">
<%= f.label :description, "Bio" %>
<%= f.text_area :description, class: 'form-control', cols: "30", rows: "10" %>
</div>
Picture: <%= f.file_field :picture %>
<%= f.submit 'Sign up', :class => 'button right' %>
<% end %>
</div>
My edit is similar:
<div class="authform">
<h3>Edit <%= resource_name.to_s.humanize %></h3>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, :role => 'form', multipart: true }) do |f| %>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
</div>
<fieldset>
<p>Leave these fields blank if you don't want to change your password.</p>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, :autocomplete => 'off', class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
</fieldset>
<fieldset>
<p>You must enter your current password to make changes.</p>
<div class="form-group">
<%= f.label :current_password %>
<%= f.password_field :current_password, class: 'form-control' %>
</div>
</fieldset>
Picture: <%= f.file_field :picture %>
<%= f.submit 'Update', :class => 'button right' %>
<% end %>
</div>
<div class="authform">
<h3>Cancel Account</h3>
<p>Unhappy? We'll be sad to see you go.</p>
<%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete, :class => 'button right' %>
</div>
Here is my UsersController :
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :admin_only, :except => :show
def index
#users = User.all
end
def show
#user = User.find(params[:id])
unless (current_user.admin? || current_user == #user)
unless #user == current_user
redirect_to :back, :alert => "Access denied."
end
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def admin_only
unless current_user.admin?
redirect_to :back, :alert => "Access denied."
end
end
def secure_params
params.require(:user).permit(:role, :picture, :name, :email, :password)
end
end
And finally I am including my UserModel:
class User < ActiveRecord::Base
enum role: [:user, :vip, :admin, :manager]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
mount_uploader :picture, PictureUploader
has_many :projects, dependent: :destroy
end
I think I am in the right path:
https://github.com/carrierwaveuploader/carrierwave/wiki/how-to:-use-carrierwave-with-devise
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?
In my rails app, if I go to localhost:3000/users/edit to update user
<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 :email %><br />
<%= f.text_field :email%></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 %>
<h3>Cancel my account</h3>
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %></p>
<%= link_to "Back", :back %>
It 's alright, but if I move this code ((or render this view) to another view of another controller it 'll have an error :"undefined local variable or method `resource' for #<#:0x3b27478>"
I don't know how to fix it, any help really appreciated.
resource is a variable set by the Devise gem. In order to move the above code somewhere else, it means that you will have to take care of setting the resource variable yourself.
Basically a rails form_for wants to take the instance of the object you want to create / edit.
<%= form_for instance_object ...
In your case you'll need to first fetch the user you want to edit (or use current_user) and then give it to the form_for helper method:
<%= form_for current_user ...
or by setting the #user instance variable in your controller first:
def new
#user = User.new
end
def edit
#user = User.find(some_id)
end
Then in your view:
<%= form_for #user ...
Don't forget to replace all the resource occurences by your instance variable.
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 %>