2013-04-04 4 views
7

Potrzebuję dostosować zapomniane instrukcje dotyczące haseł do obsługi subdomeny. Śledzę z instrukcjami na stronie opracować przesłonić mailera, kontroler i dodać pomocnika subdomeny itp wymienione:Railsy przekazują request.subdomain do niestandardowego layoutu pocztowego Devise

controllers/password_controller.rb

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

routes.rb

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

devise.rb

config.mailer = "UserMailer" 

koperty/użytkownik _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 

views/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> 

pomocnicy/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 

environment.rb

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

teraz ten kod jest najgorszy króla, ale po prostu nie może uzyskać wartości @domdomaina w widoku mailera. Jeśli zastąpię @domubain z zakodowanym na stałe ciągiem, to poprawny adres URL zostanie przekazany w e-mailu, więc wiem, że kod jest poprawny.

Jak uzyskać zmienną instancji @ subdomena zdefiniowaną w kontrolerze w widoku programu pocztowego?

Odpowiedz

7

Znalazłem sposób. Pomyślę, czy uda mi się znaleźć lepszy sposób bez konieczności pakowania małpich łatek i konieczności łączenia ich z subdomeną.

Zasadniczo zastąpić kontroler ten sposób:

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 

Również musiałem małpa patcha to metody na moim modelu użytkownika:

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 

iw końcu musiałem małpa plastra devise_mail metody, które znajdują się w 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 

W ten sposób zmienna @subdomain pojawiła się w szablonie wiadomości. Nie jestem zadowolony z tego rozwiązania, ale to jest punkt wyjścia. Zastanowię się nad ewentualnymi ulepszeniami.

+1

Może to się łatwiejsze, kiedy to połączył się: https://github.com/plataformatec/devise/pull/2207 –

+0

w końcu dostał szansę to sprawdzić - i to wszystko działa, zgadzają się, że czuje się trochę nieczyste, ale naprawdę nie mogę znaleźć tutaj prostego, czystego rozwiązania - dziękuję, to ogromna pomoc! –

1

Oto uaktualniony odpowiedź, że myślę rozwiązuje swoje pytanie ładnie - https://github.com/plataformatec/devise/wiki/How-To:-Send-emails-from-subdomains

W moim przypadku moja subdomeny był przechowywany w moim stole kont i oto co zrobiłem, aby umożliwić mi korzystać @resource.subdomain w moich opracować poglądów Mailer

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 
1

Dla devise 3.1 powyższe łatanie małpy w modelu użytkownika może wyglądać jak poniżej. To w przypadku, gdy twoja poddomena jest przechowywana w oddzielnym modelu (powiedzmy najemcy), który nie ma związku z innymi modelami, takimi jak konta, użytkownicy, czymkolwiek jest.(Dowiedz się jak current_tenant.subdomain)

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 
0

Rozwiązania powyżej nie będą działać ze jeśli chcesz przekazać subdomeny do „e-mail z potwierdzeniem”, a także dlatego, że jest obsługiwane wyłącznie w modelu.

Rozwiązałem oba scenariusze (zapomniałem hasła i email potwierdzający), przechowując subdomenę (lub jakikolwiek inny kontekst) klejnotem request_store, a następnie ustawiając własny program pocztowy, aby użyć tej wartości.

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 

Jedyne co potrzebne, aby zastąpić w User klasie jest metoda send_devise_notification zawierać Intel przechowywane w magazynie żądania.

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 

Oczywiście, trzeba skonfigurować devise korzystać z programu pocztowego w config/initializers/devise.rb.

Devise.setup do |config| 
    # ... 
    config.mailer = 'DeviseMailer' 
    # ... 
end 
Powiązane problemy