working on updating legacy devise code (that doesn't use devise_invitable
) , suffering inevitable headache.
the desired functionality stave off delivery of devise's confirmation email until admin invites given user, manually.
there's form on devise/registrations#new
user inputs email, , admin backend, ultimately, users#index
, there's "invite button".
it seem things have changed in recent years devise (app @ ~> 4.1.1
) since legacy business logic in admin dashboard looks users not have confirmation_token
present, , asks admin if want send users invite.
<%=(user.confirmation_token.nil? ? (link_to "send invitation", invite_user_path(user.id), {:class => 'button radius tiny'}) : (link_to "resend", invite_user_path(user.id), {:class => 'button radius tiny'})) unless user.confirmed_at.present? %>
here user
model, chock full of methods meant override default logic devise uses validate , create user.
class user < activerecord::base ... devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable has_and_belongs_to_many :roles, :join_table => :roles_users after_create :add_role def password_required? if !persisted? !(password != "") else !password.nil? || !password_confirmation.nil? end end # override devise method def confirmation_required? false end # override devise method def active_for_authentication? confirmed? || confirmation_period_valid? end def send_reset_password_instructions if self.confirmed? super else errors.add :base, "you must receive invitation before set password." end end # new function set password def attempt_set_password(params) p = {} p[:password] = params[:password] p[:password_confirmation] = params[:password_confirmation] update_attributes(p) end # new function determine whether password has been set def has_no_password? self.encrypted_password.blank? end # new function provide access protected method pending_any_confirmation def only_if_unconfirmed pending_any_confirmation {yield} end private def add_role return if roles.pluck(:name).include? "partner" free = role.find_by_name "free" roles << free save! end end
after tinkering on prod, seem confirmation token being created immediately, in when user
record created.
2016-06-06t21:49:19.283289+00:00 app[web.1]: sql (27.9ms) update `users` set `created_at` = '2016-06-06 21:49:19.172495', `updated_at` = '2016-06-06 21:49:19.172495', `id` = 37, `unconfirmed_email` = 'adminemail@gmail.com', `confirmation_token` = 'bdyrkwzg31mptfaskesu', `confirmation_sent_at` = '2016-06-06 21:49:19.252215' `users`.`id` = 37
interestingly, it's not 100% clear email actually being sent, want understand why confirmation token (and confirmation_sent_at
) still being updated immediately. bug? new? or wrong?
including confirmationscontroller
spaghetti code viewing pleasure:
class confirmationscontroller < devise::passwordscontroller # remove first skip_before_filter (:require_no_authentication) if # don't want enable logged users access confirmation page. # skip_before_filter :require_no_authentication skip_before_filter :authenticate_user! # post /resource/confirmation def create self.resource = resource_class.send_confirmation_instructions(resource_params) if successfully_sent?(resource) respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name)) else respond_with(resource) end end # put /resource/confirmation def update with_unconfirmed_confirmable if @confirmable.has_no_password? @confirmable.attempt_set_password(resource_params) if @confirmable.valid? @confirmable.update_attributes!(resource_params) do_confirm else do_show @confirmable.errors.clear #so won't render :new end else self.class.add_error_on(self, :email, :password_allready_set) end end if !@confirmable.errors.empty? render 'devise/confirmations/new' end end # /resource/confirmation?confirmation_token=abcdef def show with_unconfirmed_confirmable if @confirmable.has_no_password? do_show else do_confirm end end if !@confirmable.errors.empty? render 'devise/confirmations/new' end end protected def with_unconfirmed_confirmable @confirmable = user.find_or_initialize_with_error_by(:confirmation_token, params[:confirmation_token]) self.resource = @confirmable if !@confirmable.new_record? @confirmable.only_if_unconfirmed {yield} end end def do_show @confirmation_token = params[:confirmation_token] @requires_password = true render 'devise/confirmations/show' end def do_confirm @confirmable.confirm! set_flash_message :notice, :confirmed sign_in_and_redirect(resource_name, @confirmable) end # path used after resending confirmation instructions. def after_resending_confirmation_instructions_path_for(resource_name) new_session_path(resource_name) end # path used after confirmation. def after_confirmation_path_for(resource_name, resource) after_sign_in_path_for(resource) end private def resource_params params.require(:user).permit(:first_name, :last_name, :email, :encrypted_password, :reset_password_token, :reset_password_sent_at, :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip, :created_at, :updated_at, :confirmation_token, :confirmed_at, :confirmation_sent_at) end end
adding registrationscontroller
too:
class users::registrationscontroller < devise::registrationscontroller def new super end def create session[:email] = params[:user][:email] if params[:user][:email] if user.exists?(email: params[:user][:email]) redirect_to :public_feed, flash: { notice: "welcome back." } else super end end end