module Devise::Models::Invitable

Invitable is responsible for sending invitation emails. When an invitation is sent to an email address, an account is created for it. Invitation email contains a link allowing the user to accept the invitation by setting a password (as reset password from Devise’s recoverable module).

Configuration:

invite_for: The period the generated invitation token is valid.
            After this period, the invited resource won't be able to accept the invitation.
            When invite_for is 0 (the default), the invitation won't expire.

Examples:

User.find(1).invited_to_sign_up?                    # => true/false
User.invite!(email: 'someone@example.com')          # => send invitation
User.accept_invitation!(invitation_token: '...')    # => accept invitation with a token
User.find(1).accept_invitation!                     # => accept invitation
User.find(1).invite!                                # => reset invitation status and send invitation again

Attributes

completing_invite[RW]
raw_invitation_token[R]
skip_invitation[RW]

Public Class Methods

confirmation_required?() click to toggle source
# File lib/devise_invitable/models.rb, line 146
def self.confirmation_required?; false; end
required_fields(klass) click to toggle source
# File lib/devise_invitable/models.rb, line 77
def self.required_fields(klass)
  fields = [:invitation_token, :invitation_created_at, :invitation_sent_at, :invitation_accepted_at,
   :invitation_limit, Devise.invited_by_foreign_key || :invited_by_id, :invited_by_type]
  fields << :invitations_count if defined?(ActiveRecord) && self < ActiveRecord::Base
  fields -= [:invited_by_type] if Devise.invited_by_class_name
  fields
end

Public Instance Methods

accept_invitation() click to toggle source

Accept an invitation by clearing invitation token and and setting invitation_accepted_at

# File lib/devise_invitable/models.rb, line 86
def accept_invitation
  self.invitation_accepted_at = Time.now.utc
  self.invitation_token = nil
end
accept_invitation!() click to toggle source

Accept an invitation by clearing invitation token and and setting invitation_accepted_at Saves the model and confirms it if model is confirmable, running invitation_accepted callbacks

# File lib/devise_invitable/models.rb, line 93
def accept_invitation!
  if self.invited_to_sign_up?
    @accepting_invitation = true
    run_callbacks :invitation_accepted do
      self.accept_invitation
      self.confirmed_at ||= self.invitation_accepted_at if self.respond_to?(:confirmed_at=)
      self.save
    end.tap do |saved|
      self.rollback_accepted_invitation if !saved
      @accepting_invitation = false
    end
  end
end
accepted_or_not_invited?() click to toggle source

Verifies whether a user has accepted an invitation (false when user is accepting it), or was never invited

# File lib/devise_invitable/models.rb, line 134
def accepted_or_not_invited?
  invitation_accepted? || !invited_to_sign_up?
end
accepting_invitation?() click to toggle source

Returns true if accept_invitation! was called

# File lib/devise_invitable/models.rb, line 124
def accepting_invitation?
  @accepting_invitation
end
add_taken_error(key) click to toggle source
# File lib/devise_invitable/models.rb, line 231
def add_taken_error(key)
  errors.add(key, :taken)
end
clear_errors_on_valid_keys() click to toggle source
# File lib/devise_invitable/models.rb, line 201
def clear_errors_on_valid_keys
  self.class.invite_key.each do |key, value|
    self.errors.delete(key) if value === self.send(key)
  end
end
clear_reset_password_token() click to toggle source
Calls superclass method
# File lib/devise_invitable/models.rb, line 195
def clear_reset_password_token
  reset_password_token_present = reset_password_token.present?
  super
  accept_invitation! if reset_password_token_present && valid_invitation?
end
confirmation_required_for_invited?() click to toggle source
# File lib/devise_invitable/models.rb, line 219
def confirmation_required_for_invited?
  respond_to?(:confirmation_required?, true) && confirmation_required?
end
created_by_invite?() click to toggle source

Verify wheather a user is created by invitation, irrespective to invitation status

# File lib/devise_invitable/models.rb, line 114
def created_by_invite?
  invitation_created_at.present?
end
deliver_invitation(options = {}) click to toggle source

Deliver the invitation email

# File lib/devise_invitable/models.rb, line 208
def deliver_invitation(options = {})
  generate_invitation_token! unless @raw_invitation_token
  self.update_attribute :invitation_sent_at, Time.now.utc unless self.invitation_sent_at
  send_devise_notification(:invitation_instructions, @raw_invitation_token, options)
end
encrypted_invitation_token() click to toggle source

provide alias to the encrypted invitation_token stored by devise

# File lib/devise_invitable/models.rb, line 215
def encrypted_invitation_token
  self.invitation_token
end
invitation_accepted?() click to toggle source

Verifies whether a user accepted an invitation (false when user is accepting it)

# File lib/devise_invitable/models.rb, line 129
def invitation_accepted?
  !accepting_invitation? && invitation_accepted_at.present?
end
invitation_due_at() click to toggle source
# File lib/devise_invitable/models.rb, line 223
def invitation_due_at
  return nil if (self.class.invite_for == 0 || self.class.invite_for.nil?)
  #return nil unless self.class.invite_for

  time = self.invitation_created_at || self.invitation_sent_at
  time + self.class.invite_for
end
invitation_taken?() click to toggle source
# File lib/devise_invitable/models.rb, line 235
def invitation_taken?
  !invited_to_sign_up?
end
invite!(invited_by = nil, options = {}) { |self| ... } click to toggle source

Reset invitation token and send invitation again

# File lib/devise_invitable/models.rb, line 139
def invite!(invited_by = nil, options = {})
  # This is an order-dependant assignment, this can't be moved
  was_invited = invited_to_sign_up?

  # Required to workaround confirmable model's confirmation_required? method
  # being implemented to check for non-nil value of confirmed_at
  if new_record_and_responds_to?(:confirmation_required?)
    def self.confirmation_required?; false; end
  end

  yield self if block_given?
  generate_invitation_token if no_token_present_or_skip_invitation?

  run_callbacks :invitation_created do
    self.invitation_created_at = Time.now.utc
    self.invitation_sent_at = self.invitation_created_at unless skip_invitation
    self.invited_by = invited_by if invited_by

    # Call these before_validate methods since we aren't validating on save
    self.downcase_keys if new_record_and_responds_to?(:downcase_keys)
    self.strip_whitespace if new_record_and_responds_to?(:strip_whitespace)

    validate = options.key?(:validate) ? options[:validate] : self.class.validate_on_invite
    if save(validate: validate)
      self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
      deliver_invitation(options) unless skip_invitation
    end
  end
end
invited_to_sign_up?() click to toggle source

Verifies whether a user has been invited or not

# File lib/devise_invitable/models.rb, line 119
def invited_to_sign_up?
  accepting_invitation? || (persisted? && invitation_token.present?)
end
password_required?() click to toggle source

Enforce password when invitation is being accepted

Calls superclass method
# File lib/devise_invitable/models.rb, line 187
def password_required?
  (accepting_invitation? && self.class.require_password_on_accepting) || super
end
rollback_accepted_invitation() click to toggle source
# File lib/devise_invitable/models.rb, line 107
def rollback_accepted_invitation
  self.invitation_token = self.invitation_token_was
  self.invitation_accepted_at = nil
  self.confirmed_at = nil if self.respond_to?(:confirmed_at=)
end
send_password_change_notification?() click to toggle source

Prevent password changed email when accepting invitation

Calls superclass method
# File lib/devise_invitable/models.rb, line 182
def send_password_change_notification?
  super && !accepting_invitation?
end
unauthenticated_message() click to toggle source
Calls superclass method
# File lib/devise_invitable/models.rb, line 191
def unauthenticated_message
  block_from_invitation? ? :invited : super
end
valid_invitation?() click to toggle source

Verify whether a invitation is active or not. If the user has been invited, we need to calculate if the invitation time has not expired for this user, in other words, if the invitation is still valid.

# File lib/devise_invitable/models.rb, line 172
def valid_invitation?
  invited_to_sign_up? && invitation_period_valid?
end
valid_password?(password) click to toggle source

Only verify password when is not invited

Calls superclass method
# File lib/devise_invitable/models.rb, line 177
def valid_password?(password)
  super unless !accepting_invitation? && block_from_invitation?
end

Protected Instance Methods

block_from_invitation?() click to toggle source
# File lib/devise_invitable/models.rb, line 241
def block_from_invitation?
  invited_to_sign_up?
end
generate_invitation_token() click to toggle source

Generates a new random token for invitation, and stores the time this token is being generated

# File lib/devise_invitable/models.rb, line 271
def generate_invitation_token
  raw, enc = Devise.token_generator.generate(self.class, :invitation_token)
  @raw_invitation_token = raw
  self.invitation_token = enc
end
generate_invitation_token!() click to toggle source
# File lib/devise_invitable/models.rb, line 277
def generate_invitation_token!
  generate_invitation_token && save(validate: false)
end
invitation_period_valid?() click to toggle source

Checks if the invitation for the user is within the limit time. We do this by calculating if the difference between today and the invitation sent date does not exceed the invite for time configured. Invite_for is a model configuration, must always be an integer value.

Example:

# invite_for = 1.day and invitation_sent_at = today
invitation_period_valid?   # returns true

# invite_for = 5.days and invitation_sent_at = 4.days.ago
invitation_period_valid?   # returns true

# invite_for = 5.days and invitation_sent_at = 5.days.ago
invitation_period_valid?   # returns false

# invite_for = nil
invitation_period_valid?   # will always return true
# File lib/devise_invitable/models.rb, line 264
def invitation_period_valid?
  time = invitation_created_at || invitation_sent_at
  self.class.invite_for.to_i.zero? || (time && time.utc >= self.class.invite_for.ago)
end
new_record_and_responds_to?(method) click to toggle source
# File lib/devise_invitable/models.rb, line 281
def new_record_and_responds_to?(method)
  self.new_record? && self.respond_to?(method, true)
end
no_token_present_or_skip_invitation?() click to toggle source
# File lib/devise_invitable/models.rb, line 285
def no_token_present_or_skip_invitation?
  self.invitation_token.nil? || (!skip_invitation || @raw_invitation_token.nil?)
end