Skip to content

Customizing for different Invite use cases (emails etc.)

prathamesh-sonpatki edited this page Dec 10, 2014 · 23 revisions

Sometimes you want to be able to send different invites for a variety of different reasons. Eg. Inviting a VIP user vs Inviting a regular user. Here is an example of doing it for a "friend" and for a "guest"

in your devise model, put

attr_writer :invitation_instructions

def deliver_invitation
 if @invitation_instructions.present?
   ::Devise.mailer.send(@invitation_instructions, self).deliver
 else
   super
 end
end

def self.invite_guest!(attributes={}, invited_by=nil)
 self.invite!(attributes, invited_by) do |invitable|
   invitable.invitation_instructions = :guest_invitation_instructions
 end
end

def self.invite_friend!(attributes={}, invited_by=nil)
 self.invite!(attributes, invited_by) do |invitable|
   invitable.invitation_instructions = :friend_invitation_instructions
 end
end

From here if you run User.invite_guest!(:email => '[email protected]') you should get an error that says that Devise::Mailer has no method such as guest_invitation_instructions.

The way to fix this is to create your own mailer class that inherits from Devise::Mailer, and create those methods yourself. EG.

class InviteMailer < Devise::Mailer

  def guest_invitation_instructions(record, opts={})
    devise_mail(record, :guest_invitation_instructions, opts)
  end

  def friend_invitation_instructions(record, opts={})
    devise_mail(record, :friend_invitation_instructions, opts)
  end

end

in your devise.rb add the line

config.mailer = "InviteMailer"

to make invite mailer your default class to send emails (wont override your other emails, which will still be sent by devise mailer)

Lastly, create your views in views/invite_mailer/guest_invitation_instructions and you should be all set!

NOTE: When you create custom mailer (for example "InviteMailer") old devise mailer views should be moved to new mailer directory (just copy files from /app/views/devise/mailer/ to /app/views/invite_mailer/).

Customizing email headers

Sometimes you might want to add a dynamic email header instead of pulling something statically from config/locals. For example, a personalized custom subject header. Here's an easy way to achieve this in devise older than 2.2.0, for >= 2.2.0 you must overwrite mailer and customize it:

class User < ActiveRecord::Base
  #... regular implementation ...
  
  # This method is called interally during the Devise invitation process. We are
  # using it to allow for a custom email subject. These options get merged into the
  # internal devise_invitable options. Tread Carefully.
  #
 def headers_for(action)
    return {} unless invited_by && action == :invitation_instructions
    { subject: "#{invited_by.full_name} has given you access to their account" }
  end
end

Allowing users to create a custom invite message

Sometimes you may want to allow users to customize the message being sent, e.g. add a personal greeting to the standard message.

To accomplish this you can use your own mailer instead of having devise email things for you.

    class User < ActiveRecord::Base
      attr_reader :raw_invitation_token
    end

    class InvitationsController < Devise::InvitationsController
      def create
        @from    = params[:from]
        @subject = params[:invite_subject]
        @content = params[:invite_content]
        
        @user = User.invite!(params[:user], current_user) do |u|
          u.skip_invitation = true
        end

        NotificationMailer.invite_message(@user, @from, @subject, @content).deliver
        @user.invitation_sent_at = Time.now.utc # mark invitation as delivered

        if @user.errors.empty?
          flash[:notice] = "successfully sent invite to #{@user.email}"
          respond_with @user, :location => root_path
        else
          render :new
        end
      end
    end

    class NotificationMailer < ActionMailer::Base
      def invite_message(user, from, subject, content)
        @user = user
        @token = user.raw_invitation_token
        invitation_link = accept_user_invitation_url(:invitation_token => @token)

        mail(:from => from, :bcc => from, :to => @user.email, :subject => subject) do |format|
          content = content.gsub '{{first_name}}', user.first_name
          content = content.gsub '{{last_name}}', user.first_name
          content = content.gsub '{{full_name}}', user.full_name
          content = content.gsub('{{invitation_link}}', invitation_link)
          format.text do
            render :text => content
          end
        end
      end
    end

The raw_invitation_token only exists in newer versions of devise_invitable (compatible with devise >= 3.1).

Note that you must send the email with the user object instance that generated the token, as user.raw_invitation_token is available only to the instance and is not persisted in the database.

We are essentially skipping the devise invitation email process all together and using our own mailer. We take the content as a parameter and send it as the body of the email. We even substitute placeholders with their actual values.

This solution will give you a lot of flexibility to do whatever you want with emails. You can even add a tracking pixel to it if you would like.