Rails 设计重置 :confirmed_at 用户电子邮件更改时的字段
Rails devise reset :confirmed_at field when user email is changed
当用户更改他们的电子邮件地址时,不会确认新电子邮件。但是设计没有 update/reset confirmed_at 字段。
我试过:
before_update :updateEmailConfirmed, if: :email_changed?
def updateEmailConfirmed
# check if there is an unconfirmed_email
user = User.find(id)
if !user.unconfirmed_email.nil?
# set confirmed_at to nil
self.update!(confirmed_at:nil)
end
end
我知道 :confirmed_at
字段用于任何确认,因此它按预期工作。但是我正在使用这个字段来跟踪电子邮件是否已经过验证。
目前,我在我的用户模型中添加了一个名为 :email_confirmed
的额外字段,类型为 bool,我将其设置为 true/false,具体取决于当前是否:email
字段已经过验证。
我的问题是,Devise 模块中是否有任何内置的东西可以让我在不向我的用户 table 引入任何新列和修改我的用户 class.[=18 的情况下执行此操作=]
更新1.)
这是为我的用户模型设置的标签:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
protected
def confirmation_required?
false
end
end
这是我的用户 table 的样子:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.boolean "verified", default: false
t.index ["confirmation_token"], name: "index_users_on_confirmation_token",
unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name:
"index_users_on_reset_password_token", unique: true
end
我能想到的最好的方法是覆盖 Devise 提供的 Devise::Mailer
和 Confirmation::Controller
。
我意识到我可以在发送到用户电子邮件的 confirmation_url 中传递一个类型参数。
class DeviseMailer < Devise::Mailer
def confirmation_instructions(record, token, opts={})
# determine if this request is to change the email
# or verify the existing email
if !record.unconfirmed_email.nil?
type = "change_email"
else
type = "verify_email"
end
# get the link for the confirmation link and append
# the type paramater to the end
confirmation_link = Rails.application
.routes.url_helpers
.user_confirmation_url(confirmation_token: token).to_s + "&type="+type.to_s
.
.
.
# send email with new confirmation link
end
end
然后在另一端,当有人点击确认链接时,我可以通过查看参数来区分请求的类型:
class ConfirmationsController < Devise::ConfirmationsController
def show
# confirm user
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
if resource.errors.empty?
# sign in user if they are not already signed in
sign_in(resource_name, resource)
# find out what kind of request this confirmation was for
type = params[:type]
if type == "verify_email"
flash[:confirmed] = "Email successfully confirmed!"
# update verified field which is used to keep track if the :email field has been verified
current_user.update(verified:true)
elsif type == "change_email"
flash[:confirmed] = "Email successfully updated!"
# update verified field which is used to keep track if the :email field has been verified
current_user.update(verified:false)
# send confirmation instructions for this new email so we can verify it
current_user.send_confirmation_instructions
end
respond_with_navigational(resource){ redirect_to root_path }
else
p resource.errors
flash[:unconfirmed] = "Something went wrong trying to confirm your email? Contact contact@email.com"
respond_with_navigational(resource.errors, status: :unprocessable_entity){ redirect_to edit_user_registration_path }
end
end
private
def after_confirmation_path_for(resource_name, resource)
sign_in(resource) # In case you want to sign in the user
root_path
end
end
当用户更改他们的电子邮件地址时,不会确认新电子邮件。但是设计没有 update/reset confirmed_at 字段。
我试过:
before_update :updateEmailConfirmed, if: :email_changed?
def updateEmailConfirmed
# check if there is an unconfirmed_email
user = User.find(id)
if !user.unconfirmed_email.nil?
# set confirmed_at to nil
self.update!(confirmed_at:nil)
end
end
我知道 :confirmed_at
字段用于任何确认,因此它按预期工作。但是我正在使用这个字段来跟踪电子邮件是否已经过验证。
目前,我在我的用户模型中添加了一个名为 :email_confirmed
的额外字段,类型为 bool,我将其设置为 true/false,具体取决于当前是否:email
字段已经过验证。
我的问题是,Devise 模块中是否有任何内置的东西可以让我在不向我的用户 table 引入任何新列和修改我的用户 class.[=18 的情况下执行此操作=]
更新1.)
这是为我的用户模型设置的标签:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
protected
def confirmation_required?
false
end
end
这是我的用户 table 的样子:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.boolean "verified", default: false
t.index ["confirmation_token"], name: "index_users_on_confirmation_token",
unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name:
"index_users_on_reset_password_token", unique: true
end
我能想到的最好的方法是覆盖 Devise 提供的 Devise::Mailer
和 Confirmation::Controller
。
我意识到我可以在发送到用户电子邮件的 confirmation_url 中传递一个类型参数。
class DeviseMailer < Devise::Mailer
def confirmation_instructions(record, token, opts={})
# determine if this request is to change the email
# or verify the existing email
if !record.unconfirmed_email.nil?
type = "change_email"
else
type = "verify_email"
end
# get the link for the confirmation link and append
# the type paramater to the end
confirmation_link = Rails.application
.routes.url_helpers
.user_confirmation_url(confirmation_token: token).to_s + "&type="+type.to_s
.
.
.
# send email with new confirmation link
end
end
然后在另一端,当有人点击确认链接时,我可以通过查看参数来区分请求的类型:
class ConfirmationsController < Devise::ConfirmationsController
def show
# confirm user
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
if resource.errors.empty?
# sign in user if they are not already signed in
sign_in(resource_name, resource)
# find out what kind of request this confirmation was for
type = params[:type]
if type == "verify_email"
flash[:confirmed] = "Email successfully confirmed!"
# update verified field which is used to keep track if the :email field has been verified
current_user.update(verified:true)
elsif type == "change_email"
flash[:confirmed] = "Email successfully updated!"
# update verified field which is used to keep track if the :email field has been verified
current_user.update(verified:false)
# send confirmation instructions for this new email so we can verify it
current_user.send_confirmation_instructions
end
respond_with_navigational(resource){ redirect_to root_path }
else
p resource.errors
flash[:unconfirmed] = "Something went wrong trying to confirm your email? Contact contact@email.com"
respond_with_navigational(resource.errors, status: :unprocessable_entity){ redirect_to edit_user_registration_path }
end
end
private
def after_confirmation_path_for(resource_name, resource)
sign_in(resource) # In case you want to sign in the user
root_path
end
end