8

我需要调整忘记密码的说明来处理子域。我已按照设计站点上的说明覆盖邮件程序、控制器并添加子域帮助程序等,如下所示:

控制器/password_controller.rb

class PasswordsController < Devise::PasswordsController
  def create
    @subdomain = request.subdomain
    super
  end
end

路线.rb

devise_for :users, controllers: { passwords: 'passwords' }

设计.rb

config.mailer = "UserMailer"

邮件程序/user_mailer.rb

class UserMailer < Devise::Mailer
  helper :application # gives access to all helpers defined within `application_helper`.

  def confirmation_instructions(record, opts={})
    devise_mail(record, :confirmation_instructions, opts)
  end

  def reset_password_instructions(record, opts={})
    devise_mail(record, :reset_password_instructions, opts)
  end

  def unlock_instructions(record, opts={})
    devise_mail(record, :unlock_instructions, opts)
  end

end

意见/user_mailer/reset_password_instructions.html.erb

<p>Hello <%= @resource.email %>!</p>

<p>Someone has requested a link to change your password. You can do this through the link below.</p>
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token, :subdomain => @subdomain) %></p>

<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>

助手/subdomain_helper.rb

module SubdomainHelper
  def with_subdomain(subdomain)
    subdomain = (subdomain || "")
    subdomain += "." unless subdomain.empty?
    host = Rails.application.config.action_mailer.default_url_options[:host]
    [subdomain, host].join
  end

  def url_for(options = nil)
    if options.kind_of?(Hash) && options.has_key?(:subdomain)
      options[:host] = with_subdomain(options.delete(:subdomain))
    end
    super
  end
end

应用程序.rb

config.to_prepare do
  Devise::Mailer.class_eval do 
    helper :subdomain
  end
end

现在,这段代码一切正常,但它无法在邮件视图中获取@subdomain 的值。如果我用硬编码字符串替换@subdomain,那么正确的 url 会在电子邮件中传递,所以我知道代码都是正确的。

如何将控制器中定义的实例变量@subdomain 放入邮件程序视图中?

4

4 回答 4

7

我找到了办法。我会想如果我能找到一个更好的方法,而不必修补东西并且不必将它链接到子域。

基本上,我覆盖控制器这样做:

class PasswordsController < Devise::PasswordsController
  def create
    subdomain = request.subdomain
    @user = User.send_reset_password_instructions(params[:user].merge(subdomain: subdomain))

    if successfully_sent?(@user)
      respond_with({}, :location => after_sending_reset_password_instructions_path_for(:user))
    else
      respond_with(@user)
    end
  end
end

另外,我不得不在我的用户模型上修改这个方法:

def send_reset_password_instructions(subdomain)
  generate_reset_password_token! if should_generate_reset_token?
  send_devise_notification(:reset_password_instructions, subdomain: subdomain)
end

def self.send_reset_password_instructions(attributes={})
  recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
  recoverable.send_reset_password_instructions(attributes[:subdomain]) if recoverable.persisted?
  recoverable
end

最后,我不得不devise_mail修改位于 Devise 内部的修补方法。

  Devise::Mailer.class_eval do
    def devise_mail(record, action, opts={})
      initialize_from_record(record)
      initialize_subdomain(opts.delete(:subdomain)) # do this only if the action is to recover a password.
      mail headers_for(action, opts)
    end

    def initialize_subdomain(subdomain)
      @subdomain = instance_variable_set("@subdomain", subdomain)
    end
  end

这样做,@subdomain变量出现在邮件模板上。我对这个解决方案不满意,但这是一个起点。我会考虑对其进行任何改进。

于 2013-04-14T01:08:55.587 回答
1

这是一个更新的答案,我认为可以很好地解决您的问题 - https://github.com/plataformatec/devise/wiki/How-To:-Send-emails-from-subdomains

在我的情况下,我的子域存储在我的 Accounts 表中,这就是我允许我@resource.subdomain在我的设计邮件视图中使用的操作

class User < ActiveRecord::Base  
  belongs_to :account

  # This allows me to do something like @user.subdomain
  def subdomain
    account.subdomain
  end
end

class Account < ActiveRecord::Base
  has_many :users
end
于 2013-05-30T04:16:21.987 回答
1

对于设计 3.1,用户模型中的上述猴子补丁可以如下所示。如果您的子域存储在一个单独的模型(比如租户)中,该模型与帐户、用户等其他模型无关。

def send_reset_password_instructions(subdomain)
  raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)

  self.reset_password_token   = enc
  self.reset_password_sent_at = Time.now.utc
  self.save(:validate => false)

  send_devise_notification(:reset_password_instructions, raw, {subdomain: subdomain})
  raw
end

def self.send_reset_password_instructions(attributes={})
  recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
  recoverable.send_reset_password_instructions(attributes[:subdomain]) if recoverable.persisted?
  recoverable
end
于 2013-10-18T06:35:34.570 回答
0

如果您也想将子域传递给“确认电子邮件”,则上述解决方案将无法使用,因为它完全在模型中处理。

我已经解决了这两种情况(忘记密码和确认电子邮件),方法是使用request_store gem 存储子域(或任何其他上下文),然后设置我自己的邮件程序以使用此值。

class DeviseMailer < Devise::Mailer
  include Devise::Controllers::UrlHelpers
  default template_path: "devise/mailer"

  protected
  def devise_mail(record, action, opts={})
    @subdomain = opts.delete(:subdomain)
    super
  end
end

User在类中唯一需要重写的是send_devise_notification包含存储在请求存储中的 intel 的方法。

class User < ActiveRecord::Base
  # ...
  protected
  def send_devise_notification(notification, *args)
    opts = args.extract_options!
    opts[:subdomain] = RequestStore.store[:subdomain]
    super(notification, *args, opts)
  end
end

当然,您必须配置设计以在config/initializers/devise.rb.

Devise.setup do |config|
  # ...
  config.mailer = 'DeviseMailer'
  # ...
end
于 2015-07-15T12:28:08.163 回答