Search code examples
ruby-on-railsrubydevisedevise-confirmable

Rails 4 - Devise - Confirmable subject to approval


I'm trying to learn how to read rubydocs. I'm sure there is something fundamentally basic that I have missed in trying to use these documents, but for now, I'm so frustrated that information is being presented to me, but without any form of intelligible user guide that goes along with it.

I want to amend my devise, so that users can nominate a group they want to join - in this case, an organisation where their email is changed to an organisation email, pending confirmation by the organisation owner.

I think this function (which I have copied below from the devise ruby docs) is going to help.

#postpone_email_change_until_confirmation_and_regenerate_confirmation_token ⇒ Object (protected)

253
# File 'lib/devise/models/confirmable.rb', line 247

def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
  @reconfirmation_required = true
  self.unconfirmed_email = self.email
  self.email = self.email_was
  self.confirmation_token = nil
  generate_confirmation_token
end

My problem is that I don't understand how it works or how to incorporate it in my app.

Do I add it to my user model (just copy & paste) and then put a line in my user controller for the update action, so that any change of email does each of those the steps listed in the method?

If I do that, what name do I give to the controller call?

In user.rb I have tried:

 def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
      @reconfirmation_required = true
      self.unconfirmed_email = self.email
      self.email = self.email_was
      self.confirmation_token = nil
      generate_confirmation_token
    end

In user controller - update action, I have tried:

if @user.update(user_params)
user.postpone_email_change_until_confirmation_and_regenerate_confirmation_token

Im clearly trying to do this incorrectly. What's not clear to me is how I'm supposed to use this function.

Can anyone see where I've gone wrong?


Solution

  • That method is in Confirmable concern. So, all you need is to define it inside your user.rb

    For example

    # Extensions
    devise :database_authenticatable, :registerable, :confirmable,
           :recoverable, :rememberable, :trackable, :validatable, 
           :lastseenable, :timeoutable
    

    Consider to the :confirmable symbol.

    Then if you will change email before_update and after_commit callbacks will be called if all conditions will return true value

    You can't call protected instance method from anywhere.

    In case of protected methods, you can call them from the scope of any object belonging to the same class. This means that when you pass a Person object p1 as an argument to a method on another Person object p2, you can access all the protected methods of p1 within that method.

    from here

    module Confirmable
      extend ActiveSupport::Concern
    
      included do
        # ...
        after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
        after_commit :send_reconfirmation_instructions, on: :update, if: :reconfirmation_required?
        #...
        before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
      end
    

    here is a place where callback is called.

    And then devise will send reconfirmation email to user.unconfirmed_email

    You may need to override reconfirmation instructions email. Here is an example:

    # app/mailers/devise/mailer.rb
    
    if defined?(ActionMailer)
      class Devise::Mailer < Devise.parent_mailer.constantize
        include Devise::Mailers::Helpers
    
        def confirmation_instructions(record, token, opts = {})
          @token = token
          if record.pending_reconfirmation?
            devise_mail(record, :reconfirmation_instructions, opts)
          else
            devise_mail(record, :confirmation_instructions, opts)
          end
        end
      end
    end
    

    And the mailer view

    # app/views/devise/reconfirmation_instructions.text.erb
    
    Hello, <%= @resource.fullname %>!
    
    Please reconfirm your new email: <%= your_url_with_@token" %>
    
    --
    WBR, support team
    

    Hope that helps.

    UPDATE

    All you need to enable confirmation/reconfirmation is just to add needed strategy to your User model

    devise :database_authenticatable, :registerable, :confirmable,
           :recoverable, :rememberable, :trackable, :validatable, 
           :lastseenable, :timeoutable
    

    And override devise mailer

    # app/mailers/devise/mailer.rb
    
    if defined?(ActionMailer)
      class Devise::Mailer < Devise.parent_mailer.constantize
        include Devise::Mailers::Helpers
    
        def confirmation_instructions(record, token, opts = {})
          @token = token
          if record.pending_reconfirmation?
            devise_mail(record, :reconfirmation_instructions, opts)
          else
            devise_mail(record, :confirmation_instructions, opts)
          end
        end
      end
    end