actionmailer question on rails 3 - ruby-on-rails

I'm trying to convert this code
def password_reset_instructions(user)
subject "Registered"
recipients user.email
body :edit_password_reset_url => edit_password_reset_url(user.perishable_token)
end
to this code
def password_reset_instructions(user)
#user = user
mail(:to => user.email, :subject => "Registered")
end
My problem is i don't know where to put the code below.
:edit_password_reset_url => edit_password_reset_url(user.perishable_token)"
I am using authlogic on rails 3.

In Rails 3, Mailers work just like controllers. You can use the instance variable of the user in the accompanying view.

Not tested, but try this:
def password_reset_instructions(user)
#edit_password_reset_url = edit_password_reset_path(user.perishable_token)
mail(
:subject => "Password Reset Instructions",
:recipients => user.email
)
end

Related

Rails actionmailer: attachments from database

I'm using actionmailer to send mails in rails. I want to attach multiple attachments:
def prepare_attachments(languages)
attachments = {}
languages.each do |language|
next unless language.document
attachments[language.document.filename] = language.document.read
end
return attachments
end
def distribution_email(recipient, languages)
attachments = self.prepare_attachments(languages)
mail(
:to => recipient,
:subject => 'Test'
)
end
The delivered mail doesn't contain any attachment.
This is working:
def distribution_email(recipient, languages)
attachments['test.pdf'] = File.read("/tmp/test.pdf")
mail(
:to => recipient,
:subject => 'Welcome to My Awesome Site'
)
end
What am i doing wrong?
I fount the solution, one must not override attachment:
def prepare_attachments(languages)
attachments = {}
languages.each do |language|
next unless language.document
attachments[language.document.filename] = language.document.read
end
return attachments
end
def distribution_email(recipient, languages)
self.prepare_attachments(languages).each do |filename, content|
attachments[filename] = content
end
mail(
:to => recipient,
:subject => 'Test'
)
end

ActionMailer not delivering mail in development

I am trying to build a password reset email. I am Using Rails 3.2.16, and Ruby 1.9.3p327, I have read a ton of answers to this question and tried pretty much everything. I have also gone through the action mailer basics guide, and as far as i can see this should be working but its just not going well. Ill do a step by step guide of how i set it up.
firstly since i am trying to get this working in development, within development.rb Note: I have reset the application each time i edited the development.rb file.
#this is all the action mailer settings i have defined in development.rb
config.action_mailer.raise_delivery_errors = true # Set to true so that errors would be visible.
config.action_mailer.perform_deliveries = true # I read about this possible fix on SO
config.action_mailer.default_url_options = {
host: "boogle.dev"
}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.office365.com",
:port => 587,
:domain => "mpowered.co.za",
:user_name => "support#mpowered.co.za",
:password => "password",
:authentication => :login,
:enable_starttls_auto => true
}
My notifier class which inherits from ActionMailer
class Notifier < ActionMailer::Base
default from: "Mpowered - BEEtoolkit <support#mpowered.co.za>"
def deliver_password_reset_email(user)
#edit_password_reset_url = edit_password_reset_url(user.perishable_token)
#name = user.name
mail(
subject: "Password Reset Instructions",
to: user.email,
date: Time.now,
content_type: "text/html")
end
end
Within my User model i have set up the method which will send the mail along with setting up of a perishable_token
def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.deliver_password_reset_email(self)
end
The Passwords reset controller is set up like this:
class PasswordResetsController < ApplicationController
before_filter :require_no_user
before_filter :load_user_using_perishable_token, :only => [ :edit, :update ]
def new
end
def create
#user = User.find_by_email(params[:email])
if #user
#user.deliver_password_reset_instructions!
flash[:notice] = "Instructions to reset your password have been emailed to you"
render action: :new
else
flash.now[:error] = "No user was found with email address #{params[:email]}"
render action: :new
end
end
def edit
end
def update
#user.password = params[:password]
# Only if your are using password confirmation
#user.password_confirmation = params[:password]
# Use #user.save_without_session_maintenance instead if you
# don't want the user to be signed in automatically.
if #user.save
flash[:success] = "Your password was successfully updated"
redirect_to #user
else
render action: :edit
end
end
private
def load_user_using_perishable_token
#user = User.find_using_perishable_token(params[:id])
unless #user
flash[:error] = "We're sorry, but we could not locate your account"
redirect_to root_url
end
end
end
I added resources to my routes:
resources :password_resets, :only => [ :new, :create, :edit, :update ]
My views are simple:
in app/views/password_resets/new.haml.html
%br
= form_tag password_resets_path, class: 'form-inline' do
%legend Forgotten Password
%p Enter your email address and instructions to reset your password will be emailed to you:
%span.span1
= label_tag :email, 'Email'
= text_field_tag :email
= submit_tag 'Reset my password', class: 'btn'
%br
So this should send the mail once you submit a valid email.
You should then receive an email with this content: app/views/notifier/password_reset_instructions.html.haml
%h1 Password Reset Instructions
%p
A request to reset your password has been made. If you did not make
this request, simply ignore this email. If you did make this
request, please follow the link below.
= link_to "Reset Password!", #edit_password_reset_url
The link should bring you to a form where you can then save a new password and password confirmation.
app/views/password_resets/edit.html.haml
- if #user
= form_for #user, :url => password_reset_path, :method => :put do |f|
%legend Change My Password
%p Please select a new password for your account
.span8
= f.field :password, :field_type => :password_field, :label => "New password"
= f.field :password_confirmation, :field_type => :password_field
.clearfix
= f.submit "Update my password", class: 'btn'
- else
%h3 We couldn't identify your reset code
%p We're sorry, but we could not locate any accounts with reset codes that matched yours.
%p If you are having difficulties resetting your password, try copying and pasting the URL from your password reset email into your browser or restarting the password reset process.
to which you can save your new password and then login once more.. this is what i have set up in the app. but everytime i try send it by following the system, it says the email was sent but nothing ever comes. I have also tried loading up a user in the console and then running u.deliver_password_reset_instructions!
and i get this:
But still no email in my inbox. I have currently set the email address in the notifier to my own personal one so no matter what valid email address is requested, the email should come to me.
I have been hitting walls for the last 12 hours and have no idea where to turn. i am hoping i have made a balls up that a fresh pair of eyes can catch.
You need to add .deliver when calling Mailer method like this
def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.deliver_password_reset_email(self).deliver
end
Hope this helps

Getting (omniauth-facebook) and (omniauth-twitter) work

I'm using:
Ruby on Rails 4
devise 3.0.3
omniauth (1.1.4)
omniauth-facebook (1.4.1)
omniauth-twitter (1.0.0)
I recently set up my omniauth-facebook and everything works fine. Now i want to add omniauth-twitter but somehow i mess things up, pretty bad.
1.) To set up my Omniauth-Facebook i did this (in a nutshell):
gem 'omniauth'
gem 'omniauth-facebook'
2.) Added the columns "provider" and "uid" to my User model.
3.) Next, i declared the provider in my config/initializers/devise.rb:
require "omniauth-facebook"
config.omniauth :facebook, "App_ID", "App_Secret",
{:scope => 'email,offline_access',
:client_options => {:ssl => {:ca_file => 'lib/assets/cacert.pem'}},
:strategy_class => OmniAuth::Strategies::Facebook}
4.) I edited my Model User.rb
# Facebook Settings
def self.find_for_facebook_oauth(auth, signed_in_resource = nil)
user = User.where(provider: auth.provider, uid: auth.uid).first
if user.present?
user
else
user = User.create(first_name:auth.extra.raw_info.first_name,
last_name:auth.extra.raw_info.last_name,
facebook_link:auth.extra.raw_info.link,
user_name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20])
end
end
and added the attributes to devise:
:omniauth_providers => [:facebook]
5.) I edited the routes:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
THE END
Although this worked perfectly for Facebook, i tried for hours now to get this working for Twitter, and i just cant figure it out.
If someone, who has experience in this, or just knows the solution could help me set this up, i would be very thankful :)
Thank you guys, and sorry for the long Post.
In Addition
Twitter does not provide an :email Attribute so i have to Split up my User Registration Process i guess ?
My Twitter action in my User Model
# Twitter Settings
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
unless user
user = User.create(:first_name => auth[:name],
:user_name => auth[:screen_name],
:provider => auth[:provider], :uid => auth[:uid],
:password => Devise.friendly_token[0,20]
)
end
user
end
# build auth cookie hash for twitter
def self.build_twitter_auth_cookie_hash data
{
:provider => data.provider, :uid => data.uid.to_i,
:access_token => data.credentials.token, :access_secret => data.credentials.secret,
:first_name => data.name, :user_name => data.screen_name,
}
end
I had to migrate a confirmable for Users -> How To: Add :confirmable to Users
My Form's Problem, (At Least im getting to this poing now :) )
To fix your problem with the email you could just set a dummy mail, or add a second step where the user adds his/her email.
Dummy mail:
class User < ActiveRecord::Base
before_create :set_dummy_mail, if self.provider == "twitter"
private
def set_dummy_mail
self.email = "#{self.name}_email#example.com"
end
end
Or the second step option:
Modify your controller to redirect to an add email step if the provider is twitter and the email is blank. Maybe you also have to modify your validations to allow email blank on create if the provider is twitter.
UPDATE: I did it like following:
Gemfile:
gem "devise"
gem "omniauth"
gem "omniauth-facebook"
gem "omniauth-twitter"
I used:
devise version 2.2.3
omniauth 1.1.4
omniauth-facebook 1.3.0
omniauth-twitter 0.0.17
If you are using different versions, you maybe must change a few things..
routes.rb:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :user do
post "account/create" => "users/accounts#create"
end
app/models/user.rb
class User < ActiveRecord::Base
# allow email blank for first create
validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
# facebook find method
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(:first_name => auth.extra.raw_info.first_name,
:last_name => auth.extra.raw_info.last_name,
:facebook_link => auth.extra.raw_info.link,
:user_name => auth.extra.raw_info.name
:provider => auth.provider,
:uid => auth.uid, :email => auth.info.email,
:password => Devise.friendly_token[0,20]
)
user.confirm!
end
user
end
# twitter find method
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
unless user
user = User.create(:first_name => auth[:first_name], :user_name => auth[:user_name],
:provider => auth[:provider], :uid => auth[:uid],
:password => Devise.friendly_token[0,20]
)
end
user
end
# build auth cookie hash for twitter
def self.build_twitter_auth_cookie_hash data
{
:provider => data.provider, :uid => data.uid.to_i,
:access_token => data.credentials.token, :access_secret => data.credentials.secret,
:first_name => data.screen_name, :user_name => data.name,
}
end
end
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# callback action for facebook auth
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
# callback action for twitter auth
def twitter
data = session["devise.omniauth_data"] = User.build_twitter_auth_cookie_hash(request.env["omniauth.auth"])
user = User.find_for_twitter_oauth(data)
if user.confirmed? # already registered, login automatically
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect user, :event => :authentication
elsif !user.email?
flash[:error] = "You must add an email to complete your registration."
#user = user
render :add_email
else
flash[:notice] = "Please confirm your email first to continue."
redirect_to new_user_confirmation_path
end
end
end
app/views/users/omniauth_callbacks/add_email.html.erb
<%= form_for(#user, :as => "user", :url => account_create_path, :html => {:class => "form-inline"}) do |f| %>
<%= f.email_field :email, :placeholder => User.human_attribute_name(:email), :class => "input-medium" %>
<%= f.submit "Finish registration", :class => "btn btn-small" %>
<% end %>
app/controllers/users/accounts_controller.rb
class Users::AccountsController < ApplicationController
def create
data = session["devise.omniauth_data"]
data[:email] = params[:user][:email]
user = User.find_for_twitter_oauth(data)
user.email = data[:email]
if user.save
flash[:notice] = I18n.t "devise.registrations.signed_up_but_unconfirmed"
redirect_to root_path
else
flash[:error] = I18n.t "devise.omniauth_callbacks.failure", :kind => data[:provider].titleize, :reason => user.errors.full_messages.first
render "users/omniauth_callbacks/add_email"
end
end
end
Maybe you have to modify the one or other part of my solution..you also could refactor the two methods in the user model (find_for_facebook_auth, find_for_twitter_auth) to work with one dynamic method. Try it out and let me know, if you still have problems. If you find any typo, please also let me know.. Also you should write tests to check everything within your system.
Add this in gem file
gem 'omniauth-twitter'
do bundle and restart the server
after this add your app_id and secret key in config/initializer/devise.rb
require "omniauth-twitter"
config.omniauth :twitter, "app_id", "secret_key"
edit your user model
:omniauth_providers => [:facebook,:twitter]
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
Add new controller file in
app/controller/user/omniauth_callbacks_controller.rb
def twitter
#user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
else
session["devise.twitter_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
Add this link in your view file
<%= link_to 'sign in with twitter', user_omniauth_authorize_path(:twitter) %>
Changes required in user model as twitter do not return user's email id:
Create a migration file to allow null values in user's email column:
class ChangeEmailToNullInUser < ActiveRecord::Migration
def up
change_column :users, :email, :string, :null=>true
end
def down
end
end
after this you also need to override user model validations so add this in user.rb
def email_required?
false
end
Note: Before performing this you should create your app on twitter and give proper callback path. Its is important as after authentication from twitter the controller would come back to the path which you specify in your app on twitter.
let me know if you have any problem.
Mattherick has a good solution for the email but I couldn't get the before_create to work. A callback doesn't play nice with conditional if statements because anything after the comma is meant to be an options hash. Therefore:
before_save :set_dummy_email, if self.provider == "twitter"
Popped me errors.
This how I fixed this:
before_save :set_dummy_email
def set_dummy_email
self.email ||= "#{self.name}-CHANGEME#example.com"
end
This will only set an email if one is not given by the provider (ie: Twitter).
And then for a more 'universal' way of setting attributes (so you don't need a unique strategy:
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.name = auth.info.name || auth.info.nickname
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email if auth.info.email
user.save
end
end

Rails: change default sender in action mailer

I am sending email using action mailer in my rails app. But it allows only one default sender. This is my UserMailer class:
class UserMailer < ActionMailer::Base
default :from => "example#example.com"
def welcome_email(user, order)
#user = user
#order = order
mail(:to => user.email, :subject => "Your Order")
end
def signup_email(user)
#user = user
mail(:to => user.email, :subject => "Thank you.")
end
def invite_confirm(curuser,usemail,post)
#greeting = "Hi"
#user = curuser
#post = post
mail(:to => user.email, :subject => "Hello")
end
end
I tried this:
class UserMailer < ActionMailer::Base
def welcome_email(user, order)
#user = user
#order = order
mail(:to => user.email, :subject => "Your Order", :from => "abc#xyz.com")
end
def signup_email(user)
#user = user
mail(:to => user.email, :subject => "Thank you.", :from => "qwe#asd.com")
end
def invite_confirm(curuser,usemail,post)
#greeting = "Hi"
#user = curuser
#post = post
mail(:to => user.email, :subject => "Hello", :from => "zyx#asd.com")
end
end
But still it is sending email from "example#example.com"
Is there any way to change sender for each method written in UserMailer class? Am i supposed to change anywhere else?
In config/environments/development.rb and config/environments/production.rb i have this:
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => "587",
:domain => "gmail.com",
:authentication => "plain",
:user_name => "example#example.com",
:password => "example",
:enable_starttls_auto => true
}
I guess, i should not change anything here.
You can pass it as a parameter to the mail method:
def new_mail
mail from: "example#example.com", to: "user#example.com"
end
I think you want to send mail with three different emails of the for-each action. Because you use gmail, you need Sending mail from a different address.
No single vendor is optimal for all three types of email; you likely
will use several vendors.
For “company email,” that is, sending individual email to customers or
business associates, you’ll probably use Gmail or Google Apps for
Business. For a single address, you can set up a single Gmail account
to receive and send email from a different address. More likely,
you’ll want several email addresses for your company mail. For that,
use Google Apps for Business.
Send Email with Rails
I found that, this can't be done using smtp. Need to use amazon SES which allows multi sender support.
Here's what i use, it allows to make a "title" different.
class UserMailer < ActionMailer::Base
default :from => '"example" <example#domain.com>'
def send_signup_email(user)
#user = user
mail(to: #user.email, subject: 'example')
end
end

Ruby Contact Mailer not working properly

I have a simple script for my contact_mailer.rb which has a simple form as the front end with one dropdown to select purpose and has a switch-case block to choose the email ID accordingly. Now, somehow mail is always being sent to the first one i.e. when 'localization'.
Only this gets fired whatever be selected. Please explain why this may be happening. Am pasting code snippet below for reference:
class ContactUsMailer < ActionMailer::Base
default :from => "bot#mydomain.com"
def contact_us_email(name, message, purpose, email)
#name = name
#message = message
#purpose = purpose
#email = email
content_type "text/html"
case #purpose
when 'localization'
mail(:to => 'me#mydomain.com', :subject => purpose)
when 'marketing'
mail(:to => 'me#mydomain.com', :cc => 'me#mydomain.com, me#mydomain.com', :subject => purpose)
when 'network'
mail(:to => 'me.agm#gmail.com', :subject => purpose)
when 'recruitment'
mail(:to => 'me#mydomain.com', :cc => 'me#mydomain.com', :subject => purpose)
when 'general'
mail(:to => 'me#mydomain.com', :subject => purpose)
else
mail(:to => 'me#mydomain.com', :subject => purpose)
end
end
end
Thanks and Regards

Resources