2009-09-17 14:06:46 +00:00
|
|
|
module Devise
|
2009-10-09 11:30:25 +00:00
|
|
|
module Models
|
2009-10-09 12:27:44 +00:00
|
|
|
# Confirmable is responsible to verify if an account is already confirmed to
|
2009-10-13 20:01:42 +00:00
|
|
|
# sign in, and to send emails with confirmation instructions.
|
2009-10-09 12:27:44 +00:00
|
|
|
# Confirmation instructions are sent to the user email after creating a
|
2010-08-24 05:01:14 +00:00
|
|
|
# record and when manually requested by a new confirmation instruction request.
|
2009-10-22 11:49:19 +00:00
|
|
|
#
|
2010-07-15 11:01:31 +00:00
|
|
|
# == Options
|
2009-10-22 11:49:19 +00:00
|
|
|
#
|
2013-08-10 02:43:20 +00:00
|
|
|
# Confirmable adds the following options to +devise+:
|
2009-10-22 11:49:19 +00:00
|
|
|
#
|
2013-12-02 09:02:17 +00:00
|
|
|
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
|
2010-07-15 11:01:31 +00:00
|
|
|
# before confirming it. After this period, the user access is denied. You can
|
|
|
|
# use this to let your user access some features of your application without
|
|
|
|
# confirming the account, but blocking it after a certain period (ie 7 days).
|
2011-12-11 19:18:02 +00:00
|
|
|
# By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
|
2011-08-12 18:24:49 +00:00
|
|
|
# * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
|
2011-04-17 10:15:35 +00:00
|
|
|
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
2011-08-12 18:24:49 +00:00
|
|
|
# db field to be setup (t.reconfirmable in migrations). Until confirmed new email is
|
|
|
|
# stored in unconfirmed email column, and copied to email column on successful
|
|
|
|
# confirmation.
|
2012-07-22 12:02:27 +00:00
|
|
|
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
2012-07-09 12:43:12 +00:00
|
|
|
# You can use this to force the user to confirm within a set period of time.
|
2010-07-15 11:01:31 +00:00
|
|
|
#
|
|
|
|
# == Examples
|
2009-10-09 12:27:44 +00:00
|
|
|
#
|
|
|
|
# User.find(1).confirm! # returns true unless it's already confirmed
|
|
|
|
# User.find(1).confirmed? # true/false
|
|
|
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
2010-07-15 11:01:31 +00:00
|
|
|
#
|
2009-10-09 11:30:25 +00:00
|
|
|
module Confirmable
|
2010-02-17 11:35:38 +00:00
|
|
|
extend ActiveSupport::Concern
|
2012-07-09 12:43:12 +00:00
|
|
|
include ActionView::Helpers::DateHelper
|
2009-09-17 14:06:46 +00:00
|
|
|
|
2010-02-17 11:35:38 +00:00
|
|
|
included do
|
|
|
|
before_create :generate_confirmation_token, :if => :confirmation_required?
|
2013-02-22 17:43:01 +00:00
|
|
|
after_create :send_on_create_confirmation_instructions, :if => :send_confirmation_notification?
|
2013-06-13 22:19:29 +00:00
|
|
|
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, :if => :postpone_email_change?
|
|
|
|
after_update :send_reconfirmation_instructions, :if => :reconfirmation_required?
|
2009-09-17 14:06:46 +00:00
|
|
|
end
|
|
|
|
|
2013-04-18 05:33:54 +00:00
|
|
|
def initialize(*args, &block)
|
2013-08-05 16:56:07 +00:00
|
|
|
@bypass_confirmation_postpone = false
|
2013-04-18 05:33:54 +00:00
|
|
|
@reconfirmation_required = false
|
|
|
|
@skip_confirmation_notification = false
|
2013-08-05 16:56:07 +00:00
|
|
|
@raw_confirmation_token = nil
|
2013-04-18 05:33:54 +00:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2012-02-19 00:50:59 +00:00
|
|
|
def self.required_fields(klass)
|
2012-02-22 15:03:30 +00:00
|
|
|
required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]
|
2012-03-30 07:05:58 +00:00
|
|
|
required_methods << :unconfirmed_email if klass.reconfirmable
|
2012-02-22 15:03:30 +00:00
|
|
|
required_methods
|
2012-02-17 17:14:20 +00:00
|
|
|
end
|
|
|
|
|
2011-04-03 19:26:14 +00:00
|
|
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
2011-12-06 10:35:02 +00:00
|
|
|
# is already confirmed, add an error to email field. If the user is invalid
|
2011-04-03 19:26:14 +00:00
|
|
|
# add errors
|
2009-10-09 11:30:25 +00:00
|
|
|
def confirm!
|
2011-12-20 19:54:49 +00:00
|
|
|
pending_any_confirmation do
|
2012-07-23 14:05:51 +00:00
|
|
|
if confirmation_period_expired?
|
|
|
|
self.errors.add(:email, :confirmation_period_expired,
|
|
|
|
:period => Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2009-10-23 02:54:42 +00:00
|
|
|
self.confirmation_token = nil
|
2011-11-09 20:25:48 +00:00
|
|
|
self.confirmed_at = Time.now.utc
|
2011-08-12 18:13:31 +00:00
|
|
|
|
2013-07-22 17:02:53 +00:00
|
|
|
saved = if self.class.reconfirmable && unconfirmed_email.present?
|
2012-03-09 17:12:30 +00:00
|
|
|
skip_reconfirmation!
|
2011-12-20 19:54:49 +00:00
|
|
|
self.email = unconfirmed_email
|
2011-08-12 18:13:31 +00:00
|
|
|
self.unconfirmed_email = nil
|
2011-12-20 19:54:49 +00:00
|
|
|
|
|
|
|
# We need to validate in such cases to enforce e-mail uniqueness
|
|
|
|
save(:validate => true)
|
2011-08-12 18:24:49 +00:00
|
|
|
else
|
|
|
|
save(:validate => false)
|
2011-08-12 18:13:31 +00:00
|
|
|
end
|
2013-07-22 17:02:53 +00:00
|
|
|
|
|
|
|
after_confirmation if saved
|
|
|
|
saved
|
2009-10-09 11:30:25 +00:00
|
|
|
end
|
2009-09-17 14:06:46 +00:00
|
|
|
end
|
2009-10-08 22:50:46 +00:00
|
|
|
|
2009-10-09 11:30:25 +00:00
|
|
|
# Verifies whether a user is confirmed or not
|
|
|
|
def confirmed?
|
2010-07-08 06:21:14 +00:00
|
|
|
!!confirmed_at
|
2009-10-08 22:50:46 +00:00
|
|
|
end
|
|
|
|
|
2011-12-04 21:14:44 +00:00
|
|
|
def pending_reconfirmation?
|
|
|
|
self.class.reconfirmable && unconfirmed_email.present?
|
|
|
|
end
|
|
|
|
|
2009-10-09 11:30:25 +00:00
|
|
|
# Send confirmation instructions by email
|
|
|
|
def send_confirmation_instructions
|
2013-08-05 16:56:07 +00:00
|
|
|
unless @raw_confirmation_token
|
|
|
|
generate_confirmation_token!
|
|
|
|
end
|
2013-01-04 17:34:52 +00:00
|
|
|
|
|
|
|
opts = pending_reconfirmation? ? { :to => unconfirmed_email } : { }
|
2013-08-05 16:56:07 +00:00
|
|
|
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
2009-09-17 23:03:38 +00:00
|
|
|
end
|
2009-09-17 22:54:19 +00:00
|
|
|
|
2013-06-13 22:19:29 +00:00
|
|
|
def send_reconfirmation_instructions
|
|
|
|
@reconfirmation_required = false
|
|
|
|
|
|
|
|
unless @skip_confirmation_notification
|
|
|
|
send_confirmation_instructions
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Resend confirmation token.
|
|
|
|
# Regenerates the token if the period is expired.
|
2013-08-05 16:56:07 +00:00
|
|
|
def resend_confirmation_instructions
|
2012-07-23 14:05:51 +00:00
|
|
|
pending_any_confirmation do
|
|
|
|
send_confirmation_instructions
|
|
|
|
end
|
2009-10-08 03:53:04 +00:00
|
|
|
end
|
|
|
|
|
2011-03-25 14:39:08 +00:00
|
|
|
# Overwrites active_for_authentication? for confirmation
|
2011-02-06 15:34:31 +00:00
|
|
|
# by verifying whether a user is active to sign in or not. If the user
|
2009-12-20 12:53:53 +00:00
|
|
|
# is already confirmed, it should never be blocked. Otherwise we need to
|
|
|
|
# calculate if the confirm time has not expired for this user.
|
2011-03-25 14:39:08 +00:00
|
|
|
def active_for_authentication?
|
2010-05-05 21:31:11 +00:00
|
|
|
super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
|
2009-10-20 23:32:30 +00:00
|
|
|
end
|
|
|
|
|
2009-12-20 12:53:53 +00:00
|
|
|
# The message to be shown if the account is inactive.
|
|
|
|
def inactive_message
|
2010-03-10 15:13:54 +00:00
|
|
|
!confirmed? ? :unconfirmed : super
|
2009-12-20 12:53:53 +00:00
|
|
|
end
|
|
|
|
|
2009-12-14 23:30:28 +00:00
|
|
|
# If you don't want confirmation to be sent on create, neither a code
|
|
|
|
# to be generated, call skip_confirmation!
|
|
|
|
def skip_confirmation!
|
2011-11-09 20:25:48 +00:00
|
|
|
self.confirmed_at = Time.now.utc
|
2009-12-14 23:30:28 +00:00
|
|
|
end
|
|
|
|
|
2013-06-13 22:19:29 +00:00
|
|
|
# Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
|
2013-02-22 17:43:01 +00:00
|
|
|
# #skip_confirmation!, record still requires confirmation.
|
|
|
|
def skip_confirmation_notification!
|
|
|
|
@skip_confirmation_notification = true
|
|
|
|
end
|
|
|
|
|
2012-03-09 17:12:30 +00:00
|
|
|
# If you don't want reconfirmation to be sent, neither a code
|
|
|
|
# to be generated, call skip_reconfirmation!
|
|
|
|
def skip_reconfirmation!
|
2013-08-05 16:56:07 +00:00
|
|
|
@bypass_confirmation_postpone = true
|
2012-03-09 17:12:30 +00:00
|
|
|
end
|
|
|
|
|
2009-10-20 13:08:40 +00:00
|
|
|
protected
|
2009-10-09 11:30:25 +00:00
|
|
|
|
2012-02-03 16:35:55 +00:00
|
|
|
# A callback method used to deliver confirmation
|
|
|
|
# instructions on creation. This can be overriden
|
|
|
|
# in models to map to a nice sign up e-mail.
|
|
|
|
def send_on_create_confirmation_instructions
|
2013-08-05 16:56:07 +00:00
|
|
|
send_confirmation_instructions
|
2012-02-03 16:35:55 +00:00
|
|
|
end
|
|
|
|
|
2009-12-14 23:30:28 +00:00
|
|
|
# Callback to overwrite if confirmation is required or not.
|
|
|
|
def confirmation_required?
|
2010-07-08 06:21:14 +00:00
|
|
|
!confirmed?
|
2009-12-14 23:30:28 +00:00
|
|
|
end
|
|
|
|
|
2009-10-20 23:32:30 +00:00
|
|
|
# Checks if the confirmation for the user is within the limit time.
|
|
|
|
# We do this by calculating if the difference between today and the
|
|
|
|
# confirmation sent date does not exceed the confirm in time configured.
|
2011-01-24 15:44:50 +00:00
|
|
|
# Confirm_within is a model configuration, must always be an integer value.
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2009-10-20 23:32:30 +00:00
|
|
|
# Example:
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2011-12-11 19:18:02 +00:00
|
|
|
# # allow_unconfirmed_access_for = 1.day and confirmation_sent_at = today
|
2009-10-20 23:32:30 +00:00
|
|
|
# confirmation_period_valid? # returns true
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2011-12-11 19:18:02 +00:00
|
|
|
# # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 4.days.ago
|
2009-10-20 23:32:30 +00:00
|
|
|
# confirmation_period_valid? # returns true
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2011-12-11 19:18:02 +00:00
|
|
|
# # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 5.days.ago
|
2009-10-20 23:32:30 +00:00
|
|
|
# confirmation_period_valid? # returns false
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2011-12-11 19:18:02 +00:00
|
|
|
# # allow_unconfirmed_access_for = 0.days
|
2009-10-20 23:32:30 +00:00
|
|
|
# confirmation_period_valid? # will always return false
|
2009-10-23 02:54:42 +00:00
|
|
|
#
|
2013-02-13 18:12:47 +00:00
|
|
|
# # allow_unconfirmed_access_for = nil
|
|
|
|
# confirmation_period_valid? # will always return true
|
|
|
|
#
|
2009-10-20 23:32:30 +00:00
|
|
|
def confirmation_period_valid?
|
2013-02-13 18:12:47 +00:00
|
|
|
self.class.allow_unconfirmed_access_for.nil? || (confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago)
|
2009-10-09 11:30:25 +00:00
|
|
|
end
|
|
|
|
|
2012-07-22 12:02:27 +00:00
|
|
|
# Checks if the user confirmation happens before the token becomes invalid
|
|
|
|
# Examples:
|
|
|
|
#
|
|
|
|
# # confirm_within = 3.days and confirmation_sent_at = 2.days.ago
|
|
|
|
# confirmation_period_expired? # returns false
|
|
|
|
#
|
|
|
|
# # confirm_within = 3.days and confirmation_sent_at = 4.days.ago
|
|
|
|
# confirmation_period_expired? # returns true
|
|
|
|
#
|
|
|
|
# # confirm_within = nil
|
|
|
|
# confirmation_period_expired? # will always return false
|
|
|
|
#
|
|
|
|
def confirmation_period_expired?
|
2012-07-23 13:59:17 +00:00
|
|
|
self.class.confirm_within && (Time.now > self.confirmation_sent_at + self.class.confirm_within )
|
2012-07-22 12:02:27 +00:00
|
|
|
end
|
|
|
|
|
2011-12-20 19:54:49 +00:00
|
|
|
# Checks whether the record requires any confirmation.
|
|
|
|
def pending_any_confirmation
|
2012-07-23 14:05:51 +00:00
|
|
|
if (!confirmed? || pending_reconfirmation?)
|
2009-10-15 19:43:30 +00:00
|
|
|
yield
|
|
|
|
else
|
2012-07-23 13:59:17 +00:00
|
|
|
self.errors.add(:email, :already_confirmed)
|
2009-10-15 19:43:30 +00:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-08-05 16:56:07 +00:00
|
|
|
# Generates a new random token for confirmation, and stores
|
|
|
|
# the time this token is being generated
|
2009-10-18 11:14:52 +00:00
|
|
|
def generate_confirmation_token
|
2013-08-05 16:56:07 +00:00
|
|
|
raw, enc = Devise.token_generator.generate(self.class, :confirmation_token)
|
|
|
|
@raw_confirmation_token = raw
|
|
|
|
self.confirmation_token = enc
|
2009-10-18 11:14:52 +00:00
|
|
|
self.confirmation_sent_at = Time.now.utc
|
|
|
|
end
|
|
|
|
|
2010-07-14 16:03:34 +00:00
|
|
|
def generate_confirmation_token!
|
|
|
|
generate_confirmation_token && save(:validate => false)
|
|
|
|
end
|
|
|
|
|
2013-06-13 22:19:29 +00:00
|
|
|
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
2011-12-04 21:18:58 +00:00
|
|
|
@reconfirmation_required = true
|
2011-04-03 19:26:14 +00:00
|
|
|
self.unconfirmed_email = self.email
|
|
|
|
self.email = self.email_was
|
2013-08-05 16:56:07 +00:00
|
|
|
generate_confirmation_token
|
2011-04-03 19:26:14 +00:00
|
|
|
end
|
|
|
|
|
2011-08-12 16:27:57 +00:00
|
|
|
def postpone_email_change?
|
2013-08-05 16:56:07 +00:00
|
|
|
postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && !self.email.blank?
|
|
|
|
@bypass_confirmation_postpone = false
|
2011-08-12 20:51:04 +00:00
|
|
|
postpone
|
2011-04-03 19:26:14 +00:00
|
|
|
end
|
|
|
|
|
2011-12-04 21:18:58 +00:00
|
|
|
def reconfirmation_required?
|
2013-05-23 02:48:06 +00:00
|
|
|
self.class.reconfirmable && @reconfirmation_required && !self.email.blank?
|
2011-04-03 19:26:14 +00:00
|
|
|
end
|
|
|
|
|
2013-02-22 17:43:01 +00:00
|
|
|
def send_confirmation_notification?
|
2013-05-23 02:48:06 +00:00
|
|
|
confirmation_required? && !@skip_confirmation_notification && !self.email.blank?
|
2013-02-22 17:43:01 +00:00
|
|
|
end
|
|
|
|
|
2013-07-22 17:02:53 +00:00
|
|
|
def after_confirmation
|
|
|
|
end
|
|
|
|
|
2009-10-09 11:30:25 +00:00
|
|
|
module ClassMethods
|
2011-08-16 20:06:13 +00:00
|
|
|
# Attempt to find a user by its email. If a record is found, send new
|
2011-04-03 19:26:14 +00:00
|
|
|
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
|
|
|
|
# field. If no user is found, returns a new user with an email not found error.
|
2009-10-09 11:30:25 +00:00
|
|
|
# Options must contain the user email
|
2009-10-10 19:20:23 +00:00
|
|
|
def send_confirmation_instructions(attributes={})
|
2011-04-17 21:37:47 +00:00
|
|
|
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
|
|
|
|
unless confirmable.try(:persisted?)
|
|
|
|
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
|
|
|
end
|
2013-08-05 16:56:07 +00:00
|
|
|
confirmable.resend_confirmation_instructions if confirmable.persisted?
|
2009-10-09 11:30:25 +00:00
|
|
|
confirmable
|
|
|
|
end
|
|
|
|
|
2011-08-16 20:06:13 +00:00
|
|
|
# Find a user by its confirmation token and try to confirm it.
|
2009-10-18 12:16:30 +00:00
|
|
|
# If no user is found, returns a new user with an error.
|
2009-10-09 11:30:25 +00:00
|
|
|
# If the user is already confirmed, create an error for the user
|
2009-10-18 12:16:30 +00:00
|
|
|
# Options must have the confirmation_token
|
2010-03-10 15:13:54 +00:00
|
|
|
def confirm_by_token(confirmation_token)
|
2013-08-06 09:55:13 +00:00
|
|
|
original_token = confirmation_token
|
2013-08-05 16:56:07 +00:00
|
|
|
confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
2013-08-06 09:55:13 +00:00
|
|
|
|
2010-03-10 15:13:54 +00:00
|
|
|
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
2010-03-28 20:26:07 +00:00
|
|
|
confirmable.confirm! if confirmable.persisted?
|
2013-08-06 09:55:13 +00:00
|
|
|
confirmable.confirmation_token = original_token
|
2009-10-18 11:14:52 +00:00
|
|
|
confirmable
|
|
|
|
end
|
2009-10-22 21:02:03 +00:00
|
|
|
|
2011-04-03 19:26:14 +00:00
|
|
|
# Find a record for confirmation by unconfirmed email field
|
2011-04-17 21:37:47 +00:00
|
|
|
def find_by_unconfirmed_email_with_errors(attributes = {})
|
2011-12-04 21:14:44 +00:00
|
|
|
unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
|
2011-04-17 21:37:47 +00:00
|
|
|
unconfirmed_attributes = attributes.symbolize_keys
|
|
|
|
unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
|
|
|
|
find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
|
2011-04-03 19:26:14 +00:00
|
|
|
end
|
|
|
|
|
2012-07-22 12:02:27 +00:00
|
|
|
Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable, :confirm_within)
|
2009-11-23 00:32:54 +00:00
|
|
|
end
|
2009-09-17 14:06:46 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|