I have settings form. I want make so that if password field is empty, update other attributes.
Also I'm using devise. Are there some useful methods that might help me with this?
form.html.erb
<%= form_for(#user, :url => url_for(:controller => 'settings', :action => 'update')) do |f| %>
<%= f.label :email, 'Email' %>
<%= f.email_field :email %>
<%= f.label :mobile, 'Mobile' %>
<%= f.phone_field :mobile %>
<%= f.label :current_password, "Current password" %>
<%= f.password_field :current_password %>
<%= f.label :password, "New Password" %>
<%= f.password_field :password, :autocomplete => "off" %>
<%= f.label :password_confirmation, "Confirm New Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Update" %>
<% end %>
user.rb
validates :password, length: {minimum: 4}, on: :update, allow_blank: true
settings_controller.rb
def update
#user = current_user
if #user.update_attributes(user_params)
sign_in(#user, :bypass => true)
flash[:success] = "Settings were successfully updated"
respond_to do |format|
format.html {redirect_to :action=> :show}
end
else
flash[:fail] = "Settings **was not updated**"
respond_to do |format|
format.html {render :action => :show }
end
end
end
Use this:
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 his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
This is shameless copy-paste from devise wiki, it contains a lot of useful information that you can refer to - wiki
I am also using devise and in the admin namespace having a users_controller inherited from application_controller, as I wanted to keep the admin and user section separate.
The following thing worked for me.
In the User model
validates :password, :password_confirmation, presence: true, on: :create
In update action of the controller
def update
# .....
#user.update_without_password(users_params)
# ......
end
Related
I'm new to RoR and I'm trying to create a sign-up form. When I click on 'Create my account', data does not get saved to the database. I have added resources: users to my routes file. Is there something else I'm missing?
This is my view (signup.html.erb)
Sign Up
<%= form_for #user, :url => { :action => "create" } do |f| %>
<%= f.label :emp_id,"Employee ID" %><br>
<%= f.text_field :emp_id, class: 'form-control' %><br><br>
<%= f.label :password, "Password" %><br>
<%= f.password_field :password, class: 'form-control' %><br><br>
<%= f.label :password_confirmation, "Password Confirmation" %><br>
<%= f.password_field :password_confirmation, class: 'form-control' %><br><br>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
This is my controller
class UsersController < ApplicationController
def new
#user = User.new
end
def signup
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
render 'login'
else
render 'signup'
end
end
def user_params
params.require(:user).permit(:emp_id, :password)
end
end
You need to permit :password_confirmation
def user_params
params.require(:user).permit(:emp_id, :password, :password_confirmation)
end
And make sure there are no other validations like email presence or that will cause validations to fail
I'm working through Railscast: authentication from scratch in Rails 4, though the tutorial was built for Rails 3.
For this reason, some changes have had to be made from the original tutorial instructions.
Tutorial: http://railscasts.com/episodes/250-authentication-from-scratch?autoplay=true
Current running into an error
param is missing or the value is empty: session
Thoughts:
I've compared the 2 controllers below. My change from the tutorial is moving the params into a session_params method.
I understand the error is because the hash being posted does not contain a hash like "session" => { }. But I don't have a deep enough understanding of Rails to solve.
Recommended controller (rails 3)
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
My controller (converted to Rails 4)
def create
user = User.authenticate(session_params)
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
private
def session_params
params.require(:session).permit(:email, :password)
end
Up to this point I've only been using Devise for authentication, so a great thanks in advance if you can share any advice.
UPDATE
As requested, form to post session
<h1>Log in</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %> <br/>
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %> <br/>
<%= password_field_tag :password %>
</p>
<p class="button"> <%= submit_tag %> </p>
<% end %>
UPDATE 2
Also adding user.rb model
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, password_salt)
user
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Try this one:
in sessions_controller.rb
def create
#user = User.find_by(email: params[:session][:email].downcase)
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
flash[:success] = "#{#user.email}, Successfully Logged In"
redirect_to root_url, :notice => "Logged in!"
else
flash.now[:danger] = "Incorrect User/Password"
render 'new'
end
end
html form
<%= form_for :session, url: :sessions do |f| %>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<%= f.submit "Sign In", class: "button" %>
<% end %>
routes.rb
post 'sessions' => 'sessions#create'
User.rb
class User < ActiveRecord::Base
has_secure_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
end
I've created a sign up form on rails, and upon clicking my submit button to save my user details, I get the following error:
ActiveModel::ForbiddenAttributesError
for this line: #user = User.new(params[:user])in my users controller.
See code below:
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
routes.rb
Rails.application.routes.draw do
get 'users/new'
get 'pages/home'
get 'pages/howitworks'
get 'pages/about'
get 'pages/contact'
get 'pages/becomeauser'
get 'signup' => 'users#new'
resources :users
The params[:user] hash might have attributes in it that isn't a part of the User model. In your case, i'm guessing its password_confirmation.
Use strong params the next time - here
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
private:
def user_params
params.require(:user).permit(:first_name, :last_name, :email, : password)
end
This means that you have Strong Parameters enabled. I would encourage you to read at least a part of this Rails Guide. Essentially, instead of using params[] to update or create records, you should use an intermediate method to ensure that users aren't sneaking information into your requests. This is much safer. For instance, someone could submit a "created_at" attribute with your form, and then be modifying fields which are not supposed to be edited. Your approach should instead be as follows:
Create a private user_params method in the controller which permits specified attributes
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
Replace your User.create(), User.new(), and User.update() references to params[:user] with user_params
# Do this instead of #user = User.new(params[:user])
#user = User.new(user_params)
I created the New/Edit User's page so Admin can add and update Users. Now what I am trying is this: when the the Admin edits a User, the Admin has the option of not updating the password, only the email. But how will I retain the password after saving the updated email?
Edit Page:
<div><%= f.label :email, 'Email Address' %>
<%= f.email_field :email, autofocus: true, :class => "form-control", autocomplete: "off", :placeholder => "Enter email address" %></div><br />
<div><%= f.label :password %>
<%= f.password_field :password, autocomplete: "off", :placeholder => "Enter password" %></div><br />
<div><%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off", :placeholder => "Confirm password" %></div><br />
Model:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation
Controller:
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
end
You can check if the password field is empty or not, if it's empty than use devise's update_without_password method, try this
def update
#user = User.find(params[:id])
if params[:user][:password].blank?
if #user.update_without_password(params[:user].except(:password, :password_confirmation))
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
else
if #user.update_attributes(params[:user])
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
end
end
I set the Devise to allow an email only registration process that sends an email with a link to confirm account by setting password.
I would like to set the firstname and lastname at the same time than password setup (and account confirmation) but I don't know how to do.
Here is my confirmation controller (working), Crafter being my standard User.
I tried Crafter.update_attributes(params[:crafter]) with no luck.
Any help would be wonderful. TY.
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:crafter])
if #confirmable.valid?
#confirmable.update_attributes(params[:crafter])
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_allready_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
And my "confirm account" form :
<%= form_for resource, :as => resource_name, :url => update_user_confirmation_path, :html => {:method => 'patch', class: "form-horizontal", role: "form"}, :id => 'activation-form' do |f| %>
<%= f.text_field :firstname, autofocus: true, placeholder: "Firstname", class: "form-control" %>
<%= f.text_field :lastname, placeholder: "Lastname", class: "form-control" %>
<% if #requires_password %>
<%= f.password_field :password, autofocus: true, placeholder: "Choose A Password", class: "form-control" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm Password", class: "form-control" %>
<% end %>
<%= hidden_field_tag :confirmation_token,#confirmation_token %>
It's hard to tell but I'd guess you're not getting the firstname, lastname params passed to the controller.
Rails 4 uses something called strong parameters; the README there has lots of good info if you want to go beyond my example below.
In your ApplicationController you need to allow for additional params, add these lines:
app/controllers/application_controller.rb
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:firstname, :lastname, :email, :current_password, :password, :password_confirmation)
end
end
That should fix things.
In your CrafterController you could do something like this:
#confirmable.update_with_password(account_update_params)
Just do be a bit cleaner