2013-04-04 7 views
7

Ho bisogno di adattare le istruzioni della password dimenticata per gestire un sottodominio. Ho seguito le istruzioni sul sito escogitare per ignorare il mailer, controller e aggiungere un aiutante sottodominio ecc come elencato:Rails passa request.subdomain in un layout di mailing personalizzato di Devis

controller/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" 

mailer/utente _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> 

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

application.rb

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

Ora, questo codice è tutto lavoro re, ma non riesce a ottenere il valore di @subdomain nella vista mailer. Se sostituisco @subdomain con una stringa hard-coded, l'e-mail corretta viene passata nell'e-mail, quindi so che il codice è tutto corretto.

Come si ottiene la variabile di istanza @subdomain definita nel controller nella vista mailer?

risposta

7

Ho trovato un modo. Penserò se riuscirò a trovare un modo migliore senza dover scovare le patch e doverlo ricollegare al sottodominio.

Fondamentalmente, io ignorare il controller di fare questo:

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 

Inoltre, ho dovuto cerotto Monkey Questa metodi sul mio modello di utente:

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 

E, infine, ho dovuto cerotto scimmia devise_mail metodi, che vive all'interno di 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 

In questo modo, la variabile @subdomain apparso sul modello mailer. Non sono contento di questa soluzione, ma questo è un punto di partenza. Penserò a qualsiasi miglioramento su di esso.

+1

Questo può ottenere più facile quando questo ottenere fuse: https://github.com/plataformatec/devise/pull/2207 –

+0

finalmente avuto la possibilità di controllare questo fuori - ed è tutto lavoro, d'accordo ci si sente un po ' impuro ma non riesco davvero a trovare una soluzione semplice e pulita qui - grazie, questo è un grande aiuto! –

1

Ecco una risposta aggiornata che penso che risolve il problema bene - https://github.com/plataformatec/devise/wiki/How-To:-Send-emails-from-subdomains

Nel mio caso il mio sottodominio è stato immagazzinato nella mia tabella Conti ed ecco cosa ho fatto per permettermi di usare @resource.subdomain nei miei testamentaria viste 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

Per Devise 3.1 il patch di scimmia sopra nel modello utente può essere come sotto. Questo nel caso in cui il sottodominio è memorizzato in un modello separato (ad esempio tenant) che non ha alcuna relazione con altri modelli come gli account, gli utenti che cosa mai sia ..(Trovare come 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

Le soluzioni di cui sopra non funzioneranno con se si desidera passare sottodominio a "mail di conferma", come bene perché è gestita interamente in modello.

Ho risolto entrambi gli scenari (password dimenticata e e-mail di conferma) memorizzando il sottodominio (o qualsiasi altro contesto) con la gemma request_store e quindi impostando il mio mailer personale per utilizzare questo valore.

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 

L'unica cosa necessaria per sostituire in User classe è il metodo send_devise_notification per includere il processore Intel memorizzato nel negozio richiesta.

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 

Naturalmente, è necessario configurare disposizione testamentaria di utilizzare il mailer in config/initializers/devise.rb.

Devise.setup do |config| 
    # ... 
    config.mailer = 'DeviseMailer' 
    # ... 
end 
Problemi correlati