我想通了。我不确定这是否会随着 Devise 昨天在使 Devise::Mailer 将其大部分功能放入模块中所做的更新而改变。 (有关详细信息,请参阅 code 和 ticket)。
基本上归结为无法访问邮件视图中的session。因此,您必须将重定向作为变量传递。 Devise 在您的资源上使用after_create 方法(在我的情况下为User),然后发送确认电子邮件。这意味着我不能直接将会话变量传递给邮件程序。因此,为了获得此功能,我觉得这是一个非常讨厌的解决方法,但这里是代码:
要将redirect_to 变量放入邮件程序,您必须向用户添加一个变量,因此:
class User < ActiveRecord::Base
…
attr_accessor :return_to
…
end
那么你必须在第一次创建用户时设置那个变量。
我已经有一个用于注册的自定义控制器设置。 (有关如何设置的信息,请参阅设计的自述文件,或参阅@ramc 的答案以了解方向)。不过这部分做起来还是比较容易的,我只是把它加到参数里面,剩下的就自己搞定了。
class RegistrationsController < Devise::RegistrationsController
def create
params[:user][:return_to] = session[:user_return_to] if session[:user_return_to]
…
super
end
end
现在用户已经设置了一个变量return_to。我们只需要在confirmation_instructions 电子邮件中访问它。我已经重写了confirmation_instructions.html.erb 的一部分,所以我在里面添加了:
<% if @resource.return_to %>
<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token, :redirect_to => @resource.return_to) %>
<% else %>
<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
<% end %>
(对于那些不熟悉此的人,@resource 是 Devise 用来定义您的用户的变量)。
现在,一旦用户点击该链接,我们就需要重定向他们。 @ramc 的 before 过滤器适用于此:
class ConfirmationsController < Devise::ConfirmationsController
before_filter :set_redirect_location, :only => :show
def set_redirect_location
session[:user_return_to] = params[:redirect_to] if params[:redirect_to]
end
end
这将处理新用户进入受保护页面然后注册、单击确认链接并正确重定向到受保护页面的情况。
现在我们只需要处理用户执行上述操作的情况,但他们没有点击链接,而是尝试返回受保护的页面。在这种情况下,他们被要求注册/登录。他们登录,然后被要求确认他们的电子邮件,并可以选择重新发送确认电子邮件。他们输入了他们的电子邮件,现在我们需要将 redirect_to 变量放入新的确认电子邮件中。
为此,我们需要修改 ConfirmationController,类似于我们修改 RegistrationController 的方式。这次我们需要修改create方法。它开箱即用的工作方式是调用名为send_confirmation_instructions 的用户类方法。我们想重写该方法,以便将return_to 变量传递给它。
class ConfirmationsController < Devise::ConfirmationsController
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name],session[:user_return_to])
if resource.errors.empty?
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, :location => after_resending_confirmation_instructions_path_for(resource_name)
else
respond_with_navigational(resource){ render_with_scope :new }
end
end
end
与 Devise 自带的唯一不同的是create 的第一行,我们传入了两个变量。现在我们需要重写那个方法:
class User < ActiveRecord::Base
def self.send_confirmation_instructions(attributes={},redirect=nil)
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
confirmable.return_to = redirect if confirmable.persisted?
confirmable.resend_confirmation_token if confirmable.persisted?
confirmable
end
end
confirmable 成为 User 的一个实例(基于电子邮件的当前用户)。所以我们只需要设置return_to。
就是这样。