31
votes

I'm using devise confirmable. I want to give the user a link to click and resend the confirmation email. Problem is, when the user clicks the link, it isn't going to the devise controller. Is there something I'm missing in the routes.rb file? Here is my setup:

routes.rb

devise_for :users, :controllers => { :registrations => "registrations", :sessions => "sessions", :omniauth_callbacks => "authentications" }

user.rb

devise :omniauthable, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable

The view:

<a href="/users/confirmation/new" data-remote="true" data-method="post">Resend confirmation</a>

Thanks

8

8 Answers

34
votes

to send confirmation instructions to the user you find the user and then just user.send_confirmation_instructions

namespace :user do
  task :resend_confirmation => :environment do
    users = User.where('confirmation_token IS NOT NULL')
    users.each do |user|
      user.send_confirmation_instructions
    end
  end
end
10
votes
resource_params

is a method defined at devise controller which gets the controller resources specific do device resource (User) for example. definition in DeviseController

def resource_params
params.fetch(resource_name, {})
end

so in order to pass the email as a parameter you neet to include it in a user hash, so in the view instead of

link_to('resend', user_confirmation_path(email: "[email protected]"), :method => :post)                    

insert the email in a Hash

link_to('resend', user_confirmation_path(user: {:email => "[email protected]"}), :method => :post)

this way devise will pick up the email parameter

8
votes

I am sharing my solution as it's a bit of a different take, but closer to the normal user flow in my opinion (redirect to thanks page where you have a button to resend without writing email again), and you won't have to override the confirmations controller.

After signing up, the user get redirected to a 'Thanks' page. In registrations_controller:

  def after_inactive_sign_up_path_for(resource)
   session[:user_id] = resource.id
   redirect_to thanks_url
  end

in users controller, you just use the .send_confirmation_instructions on user

def thanks
    @user = User.find_by(id: session[:user_id])
end

def resend_confirmation
    user = User.find_by(id: params[:user_id])
    user.send_confirmation_instructions 
end

routes:

get '/thanks', to: 'users#thanks'
post '/resend_confirmation', to: 'users#resend_confirmation', as: 'resend_confirmation'

finally, in the 'thanks' view:

<%= button_to "Resend confirmation", resend_confirmation_path(:user_id => @user.id) %>

This could be cleaned up a bit, I'm sure, as I've just wrote it and I'm still new at Rails, but I was looking for this kind of solutions on Stack and could not find it so I thought to share it.

7
votes

As you could see on https://github.com/plataformatec/devise/blob/master/app/controllers/devise/confirmations_controller.rb#L2, the HTTP method for confirmation#new is GET, not POST. Try to remove 'data-method="post"' and see if it works.

7
votes

Pretty old post. Though, instead of sending the instructions directly, you might just want to point the user to Devise's workflow:

= link_to 'Resend confirmation', new_user_confirmation_path

That'll take the user to Devise's view requesting the email to send the confirmation instructions.

Hope it helps anyone, anyway. :)

3
votes

Here is my solution. Hope not missing something in my switch in resource_params. Note: not ajaxified

confirmation controller

class ConfirmationsController < Devise::ConfirmationsController

  # POST /resource/confirmation
  def create
    # self.resource = resource_class.send_confirmation_instructions(resource_params)
    self.resource = resource_class.send_confirmation_instructions({email: current_user.email})
    if successfully_sent?(resource)
      respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
    else
      respond_with(resource)
    end
  end

protected

  # The path used after resending confirmation instructions.
  def after_resending_confirmation_instructions_path_for(resource_name)
    root_path
  end
end

routes

devise_for :users, controllers: { confirmations: "confirmations" } 

view

<%= link_to "resend confirmation", user_confirmation_path, data: { method: :post } %>
3
votes

To resend the confirmation email, you want the post method with just 'users/confirmation'--no 'new' at the end.

Here's a form that requests the user's email and submits the confirmation resend request to Devise as follows:

form_for(resource, url: user_confirmation_path) do |f|
  .form-inputs
    = f.input :email
  .form-actions
    = f.button :submit, "Resend"
2
votes

here is my solution that will open devise form where user can enter email address and request new email with confirmation token. All devise logic regarding email verification is preserved:

app/controllers/confirmations_controller.rb

class ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
  def new
    self.resource = resource_class.new
  end
end

config/routes.rb

devise_for :users, controllers: { confirmations: "confirmations" }

app/views/devise/confirmations/new.html.erb

<h2>Resend confirmation instructions</h2>
<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
  <%= devise_error_messages! %>

  <div><%= f.label :email %><br />
  <%= f.email_field :email, :autofocus => true %></div>

  <div><%= f.submit "Resend confirmation instructions" %></div>
<% end %>

<%= render "devise/shared/links" %>