Skip to content

Customizing for different Invite use cases (emails etc.)

Michael Yagudaev edited this page Oct 10, 2013 · 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
   ::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@domain.com') 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:

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

        @user.deliver_invitation
        email = NotificationMailer.invite_message(@user, @from, @subject, @content)
      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).

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.