In order to send new confirmation instructions, an email has to be entered. I want to avoid that because my users are logged in at that moment, so there's no need for email asking. I just want to send new instructions to the current_user.email
I don't want to do client side stuff like this:
= f.email_field :email, value: current_user.email, class: "hidden"
I need a server side solution.
Thanks guys!
As per the devise codebase, sending confirmation email can be invoked on a user as follows:
user = User.find(1)
user.send_confirmation_instructions
So you don't really need to get an email from the form.
You have access to device method , this should work.
See the documentation here
routes.rb
devise_for :users, controllers: { confirmations: "confirmations" }
In view
= link_to "resend confirmation", user_confirmation_path, data: { method: :post }
I ended up with this:
First, override devise controller:
config/routes.rb
devise_for :users, controllers: { confirmations: "users/confirmations" }
controllers/users/confirmations_controller.rb
class Users::ConfirmationsController < Devise::ConfirmationsController
def create
redirect_to new_user_session_path unless user_signed_in?
if current_user.confirmed?
redirect_to root_path
else
current_user.send_confirmation_instructions
redirect_to after_resending_confirmation_instructions_path_for(:user)
end
end
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
flash[:notice] = "Instructions sent successfully."
is_navigational_format? ? root_path (or whatever route) : '/'
end
end
Then remove the email field from the view.
views/devise/confirmations/new.html.haml
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), method: :post }) do |f|
= f.submit "Resend confirmation instructions"
Thanks everyone for your answers.
Related
I have a checkbox (like a terms of use) that I need to be checked every time a user signs in.
I've seen some examples on adding a checkbox on the sign up page, adding a virtual attribute to the User model, etc.
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
%p
= f.label :username, 'Username'
= f.text_field :username
%p
= f.label :password
= f.password_field :password
%p
%span
= check_box_tag :terms_of_use
I have read the
= link_to 'Terms of Use', '#'
%p
= f.submit 'Sign in'
Here's my devise route:
devise_for :users, controllers: { sessions: 'sessions' }
And here's the custom controller:
class SessionsController < Devise::SessionsController
def create
if params[:terms_of_use]
super
else
# Not sure what to put here? Is this even the right track?
# Also, redirect the user back to the sign in page and let
# them know they must agree to the terms of use.
end
end
end
How would I go about requiring the checkbox to be selected every time a user signs in?
This blog post may help: http://hollandaiseparty.com/order-of-abstractcontrollercallbacks/
Adding a prepend_before_action should allow you to check for the terms_of_use and redirect if needed before allowing Devise to take over. Something like:
class SessionsController < Devise::SessionsController
prepend_before_action :check_terms_of_use, only: [:create]
def check_terms_of_use
unless params[:terms_of_use]
# Since it's before the session creation, root_path will take you back to login
redirect_to root_path
end
end
end
Currently I have ActionMailer send an email when a user registers, and I generate a random :sign_in_token with the user.
How can a user then click on the link sent to his email and update the users :registration_complete boolean value to TRUE?
Currently, I am able to send the link and generates a random token, but I don't know how to update the boolean value through the email.
MODELS
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :sign_in_token,
:registration_complete
###This generates my sign_in_token
def generate_sign_in_token
self.sign_in_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end
CONTROLLER
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
###Sends the User an email with sign_in_token
UserMailer.registration_confirmation(#user, login_url+"/#{#user.sign_in_token}").deliver
flash[:success] = "Please Check Your Email to Verify your Registration!"
redirect_to (verifyemail_path)
else
render 'new'
end
end
USER_MAILER
def registration_confirmation(user, login_url)
#login_url = login_url
#user = user
mail(:to => "#{user.name} <#{user.email}>", :subject => "Welcome to APP")
end
VIEWS
###Redirects User to Login Page, But how do i connect it to my activate method?
<%= link_to "Complete Registration", #login_url %>
ROUTES
match '/login/:sign_in_token', :to => 'sessions#new'
When they click a link, it takes them to a controller with an action of set_complete using a GET request, which sets the boolean value.
Something like:
def set_complete
user = User.find(params[:user])
user.update_attribute(registration_complete => true)
redirect_to login_path # or whatever your login url is...
end
For the controller action and something like this for the link:
<a href="example.com/registrations/set_complete?user=1" />
Here is a sample of what might go in the routes file:
get "/users/set_complete", :to => "users#set_complete"
You'd probably need to set the user id to whatever you want using erb, andmake a few other app-specific customizations, but this is the general idea.
Hope this helps!
I'm working through the Railscast on implementing Devise and OmniAuth (along with the Devise documentation) -- currently, I've got a site going where visitors can sign up using their facebook accounts or by filling out a form.
I'm running into trouble when users that sign up via OmniAuth try to edit their profiles, though. Devise looks for the user's current password when they submit changes to their profiles, but those that logged in with facebook don't know their passwords (they're set automatically in the user model):
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(first_name:auth.extra.raw_info.first_name,
last_name:auth.extra.raw_info.last_name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
When a user edits his information, the app should not require password confirmation if he set up his account through OmniAuth. The tutorial suggests that the handy password_required? method will help me achieve this outcome. Specifically, adding this method to the user model means that it should only return true if the user didn't sign up through OmniAuth (the provider attribute would be nil in that case):
def password_required?
super && provider.blank?
end
Thus, a piece of code like:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= render :partial => "essential_user_info_inputs", :locals => { :f => f } %>
<%= render :partial => "inessential_user_info_inputs", :locals => { :f => f } %>
<% if f.object.password_required? %>
<%= render :partial => "password_inputs", :locals => { :f => f } %>
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
<% end %>
<%= f.submit "Update" %>
<% end %>
would theoretically only display password inputs when needed. It also suggests that Devise has built in logic saying that OmniAuth users don't need to use passwords to edit their accounts. I have no idea if this is true, but the tutorial kind of makes it look like that. But when an OmniAuth user tries to edit his account, I get "Current password can't be blank." Same thing with non-OmniAuth users (this makes sense, since the password fields don't show up on those users' edit pages either).
Some poking around confirms that the password_required? method is returning false, both when the user signed up through OmniAuth and through the site's regular user signup. Even when I change it to simply run the superclass method, it returns false.
Any ideas of what's going on with the password_required method? I can't find anything about it anywhere, but I feel like that's what's tripping things up right now.
Update:
This is now working, but not using the method outlined in the Railscast, which relies on requires_password? method, a topic that I still know nothing about. Instead, I implemented the solution outlined here, as suggested here. So I am now only requiring passwords to update non-OmniAuth accounts with the code:
class Users::RegistrationsController < Devise::RegistrationsController
def update
#user = User.find(current_user.id)
email_changed = #user.email != params[:user][:email]
is_facebook_account = !#user.provider.blank?
successfully_updated = if !is_facebook_account
#user.update_with_password(params[:user])
else
#user.update_without_password(params[:user])
end
if successfully_updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
The easiest way is to overwrite the update_resource method in your RegistrationsController. This is advised by devise in their own implementation of the controller:
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
So the solution is to overwrite this method in your own controller like this:
class Users::RegistrationsController < Devise::RegistrationsController
# 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
end
I've added an update to the link below that includes my solution to the Devise/ OmniAuth change user profile/password issue and collected some helpful links:
stackoverflow - Allowing users to edit accounts without saving passwords in devise
I saw this used somewhere.
def update
params[:user].delete(:current_password)
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
if current_user.update_without_password(params[:user])
redirect_to somewhere_wicked_path, notice => "You rock"
else
render 'edit', :alert => 'you roll'
end
end
use something like this in your update method in your controller. Pretty sure that method is in Devise too.
How can I customize error messages to override devise passwords controller?
class PasswordsController < Devise::PasswordsController
def create
self.resource = resource_class.send_reset_password_instructions(params[resource_name])
if resource.errors.empty?
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, :location => home_path
else
binding.pry
flash[:devise_password_error] = (resource.errors.map do |key, value|
value.capitalize
end).flatten.join('|')
redirect_to home_path and return
end
end
def edit
self.resource = resource_class.new
resource.reset_password_token = params[:reset_password_token]
end
end
resource.errors is available in this method but it contains default messages such as Email not found and Email can't be blank. i need to customize this messages. I've tried to remove :validatable from my user model and add custom validators but this works only for my custom registrations controller derived from Devise::RegistrationsController and not for custom passwords controller.
Is there any solution?
The answer is to modify config/locales/devise.en.yml but you must add the settings, they are not there by default.
en:
activerecord:
errors:
models:
user:
attributes:
password:
confirmation: "does not match"
too_short: "is too short (minimum is %{count} characters)"
Credit for this goes to Vimsha who answered virtually the same question for me.
Devise messages are located in config/locales/devise.en.yml
I'm not sure which message you're trying to override, but that's where you want to do that.
It's not ideal, but based on this related ticket I've got it working with the following (which I know is a bit of a hack, but it works):
module DeviseHelper
def devise_error_messages!
resource.errors.full_messages.map { |msg| msg == 'Email not found' ? 'The email address you entered could not be found. Please try again with other information.' : msg }.join('<br/>')
end
end
Put this in a module called devise_helper.rb in your /app/helpers directory
Add this to your routes.rb
devise_for :users, controllers: { passwords: 'passwords' }
or
devise_for :users, :controllers => { :passwords => 'passwords' }
I'd like to have an "edit profile" page, in which the user can change the email address registered when signing up.
I'd like to have the following process:
the user has to input his password to confirm before he makes changes in the email field.
after submitting that page, the user should receive a verification mail just like Devise's default sign up.
the email change is completed as soon as the user clicks the verification token URL on the mail.
How would I do this?
I created this same flow for a site of mine. Here's an example of what you can do:
add to config/routes.rb
(note that the routing could be better, but I did this a while ago)
scope :path => '/users', :controller => 'users' do
match 'verify_email' => :verify_email, :as => 'verify_email'
match 'edit_account_email' => :edit_account_email, :as => 'edit_account_email'
match 'update_account_email' => :update_account_email, :as => 'update_account_email'
end
add to app/controllers/users_controller.rb
def edit_account_email
#user=current_user
end
def update_account_email
#user=current_user
#user.password_not_needed=true
#user.email=params[:address]
if #user.save
flash[:notice]="your login email has been successfully updated."
else
flash[:alert]="oops! we were unable to activate your new login email. #{#user.errors}"
end
redirect_to edit_user_path
end
def verify_email
#user=current_user
#address=params[:address]
UserMailer.confirm_account_email(#user, #address).deliver
end
app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
def confirm_account_email(user, address)
#user = user
#address = address
mail(
:to=>"#{user.name} <#{#address}>",
:from=>"your name <'your_email#domain.com'>",
:subject=>"account email confirmation for #{user.name}"
)
end
end
app/views/user_mailer/confirm_account_email.html.erb
<p>you can confirm that you'd like to use this email address to log in to your account by clicking the link below:</p>
<p><%= link_to('update your email', update_account_email_url(#user, :address=>#address)) %></p>
<p>if you choose not to confirm the new address, your current login email will remain active.