0

I need to modify how Devise decides if a user account is confirmed.

I have set up 2 step registration where a user enters an email then sets their password after clicking a link in their email.

This works fine but I have a scenario where a logged in user can create a user and send them a link to confirm their account. This is sent manually via a custom mailer (this includes the email address of the user who requested their account as well as the confirmation_token) which means a user is getting 2 emails if their account is created via another user, the standard devise confirmation_instructions and the custom mail 'invite'.

Following this I set the skip_confirmation! before the save which means only the email I send gets to the user but since this sets the confimed_at field a user cannot set their password as it generates an error saying account is already confirmed.

How can I change the confirmed? method to also check that the password is not null?

Here is my overridden confirmations controller for my 2 step registration process:

class ConfirmationsController < Devise::ConfirmationsController
  def show
    self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])
    super if resource.confirmed? 
  end

  def confirm
    self.resource = resource_class.find_by_confirmation_token(params[resource_name][:confirmation_token])
    if resource.update_attributes(params[resource_name].except(:confirmation_token)) && resource.password_match?
      self.resource = resource_class.confirm_by_token(params[resource_name][:confirmation_token])
      set_flash_message :notice, :confirmed
      sign_in_and_redirect(resource_name, resource)
    else
      render :action => "show"
    end
  end
end

Here is the custom mailer:

class MyMailer < Devise::Mailer   
  helper :application # gives access to all helpers defined within `application_helper`.


   def invite(sender, recipient)
        @sender = sender
        @recipient = recipient

        mail( :to => recipient.email,
              :subject => "Account created by #{sender.email}",
              :from => "noreply@noreply.com"
            )
  end

end

code in a controller where a logged in user creates a new user (showing section applicable)

.
.
.
            newContact = User.create(:email => params[:contact_email])
            newContact.skip_confirmation!
            if newContact.save
                 MyMailer.invite(current_user, newContact).deliver
                 .
                 .
                 .
             end
.
.
.
vlwills
  • 105
  • 1
  • 7

1 Answers1

0

So I have hacked this and managed to get this working so thought I would share how I got there.

So the requirement was:

1) user signs up from the registration page, email sent to confirm is a standard confirmation_instructions devise email.

2) user is created by another user, email sent is a custom mail containing the user name of creator to the sender.

I made the following changes to the above to get this working:

Added column to User table :invited:Integer

Added the following to the user model:

protected
  def send_confirmation_instructions
    if !self.invited.nil?
      self.confirmation_token = nil if reconfirmation_required?
      @reconfirmation_required = false
      generate_confirmation_token! if self.confirmation_token.blank?
    else
      self.confirmation_token = nil if reconfirmation_required?
      @reconfirmation_required = false
      generate_confirmation_token! if self.confirmation_token.blank?
      self.devise_mailer.confirmation_instructions(self).deliver
    end
  end

  def send_on_create_confirmation_instructions
    self.devise_mailer.confirmation_instructions(self).deliver if self.invited.nil?
  end

in the controller where a user is created by a logged in user changed to

.
.
.
            newContact = User.create(:email => params[:contact_email], :invited => 1)
            if newContact.save
                 MyMailer.invite(current_user, newContact).deliver
                 .
                 .
                 .
             end
.
.
.

Also added the following to the ConfirmationsController to deal with the possibility of users clicking the confirmation link again.

  def show
    self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])
    if self.resource.nil?
      redirect_to new_user_session_path, :notice => 'This link is no longer active, please sigin in or request new password'
    else
      super if resource.confirmed?
    end
  end

I am sure there is a better way but this works for what we need. if anyone want to suggest a better way then by all means be my guest.

vlwills
  • 105
  • 1
  • 7