Authlogic sends the wrong activation code - ruby-on-rails

Update: this question has been answered (see below). I'll leave it up in case anyone can benefit in the future.
I am trying to get e-mail confirmations working on Authlogic using Rails 3. http://github.com/matthooks/authlogic-activation-tutorial
Authentication is working and the activation e-mails are being generated and sent, each containing a perishable token, but the perishable tokens are incorrect, in that they do not match the one saved in the user's record.
Upon following the token in the e-mail, I get: Exception in ActivationsController#create
Note: When I manually enter the correct token from the table into the URL, it validates and redirects as it is supposed to. Therefore, the only issue is that the perishable token being generated is not the same as the one being saved.
# UserMailer
class UserMailer < ActionMailer::Base
default :from => "notifications#myapp.com"
def registration_confirmation(user)
#user = user
mail(:to => "#{user.login} <#{user.email}>", :subject => "Registered")
end
def activation_instructions(user)
subject "Activate Your Account"
from "noreply#myapp.com"
recipients user.email
sent_on Time.now
body :account_activation_url => activate_url(user.perishable_token)
end
def welcome(user)
subject "Welcome to the site!"
from "noreply#myapp.com"
recipients user.email
sent_on Time.now
body :root_url => root_url
end
end
# E-mail itself:
To activate, click here: <%= #account_activation_url %>
The error is occurring on line 5 where the system tries and fails to find User by token:
class ActivationsController < ApplicationController
before_filter :require_no_user
def create
#user = User.find_by_perishable_token(params[:activation_code], 1.week) || (raise Exception)
raise Exception if #user.active?
if #user.activate!
flash[:notice] = "Your account has been activated!"
UserSession.create(#user, false) # Log user in manually
#user.deliver_welcome!
redirect_to home_url
else
render :controller => "welcome", :action => "linklogin"
end
end
end

It's funny - sometimes the process of asking the question itself reveals the answer.
In my users#create, there are different user types, and the action sets a couple of values after the initial validated save and saves the simple changes again without validation.
My e-mail was being sent in between the first and second saves, so of course by the time the user clicks on the activation e-mail, the perishable_token has already been reset.
I moved the mailing down to after the second save, and now the activation e-mail works perfectly.
Thank you very much for any time you've spent considering this problem. :)
Cirrus

Related

Email Sign Up Confirmation with ActionMailer

Currently I have ActionMailer send an email when a user registers, and I generate a random :sign_in_token with the user.
How can a user then click on the link sent to his email and update the users :registration_complete boolean value to TRUE?
Currently, I am able to send the link and generates a random token, but I don't know how to update the boolean value through the email.
MODELS
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :sign_in_token,
:registration_complete
###This generates my sign_in_token
def generate_sign_in_token
self.sign_in_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end
CONTROLLER
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
###Sends the User an email with sign_in_token
UserMailer.registration_confirmation(#user, login_url+"/#{#user.sign_in_token}").deliver
flash[:success] = "Please Check Your Email to Verify your Registration!"
redirect_to (verifyemail_path)
else
render 'new'
end
end
USER_MAILER
def registration_confirmation(user, login_url)
#login_url = login_url
#user = user
mail(:to => "#{user.name} <#{user.email}>", :subject => "Welcome to APP")
end
VIEWS
###Redirects User to Login Page, But how do i connect it to my activate method?
<%= link_to "Complete Registration", #login_url %>
ROUTES
match '/login/:sign_in_token', :to => 'sessions#new'
When they click a link, it takes them to a controller with an action of set_complete using a GET request, which sets the boolean value.
Something like:
def set_complete
user = User.find(params[:user])
user.update_attribute(registration_complete => true)
redirect_to login_path # or whatever your login url is...
end
For the controller action and something like this for the link:
<a href="example.com/registrations/set_complete?user=1" />
Here is a sample of what might go in the routes file:
get "/users/set_complete", :to => "users#set_complete"
You'd probably need to set the user id to whatever you want using erb, andmake a few other app-specific customizations, but this is the general idea.
Hope this helps!

NoMethodError in ConfirmationController

I have been trying to solve the following problem for a couple of days. Forgive me if this is a common problem as I am new to rails and probably couldn't query the right question/keyword in stackoverflow or google.
I am building a system where a user will get an invite via email, click on a unique link, be taken to a page where he/she can accept or decline the invitation. I am getting stuck at the part where the user accepts or declines the invitation.
I've built it around two controllers: an invitations controller and a confirmations controller.The invitations controller creates a record containing a name, an email, and a uniquely generated token. The controller then emails a link with the token to the defined email. The link points to the confirmations controller and passes the unique token from the invitation. However, when clicking on the link and accepting the invitation, I get the following error:
NoMethodError in ConfirmationController#confirm
undefined method `update_attribute' for nil:NilClass
Here is some of the code for solving this issue:
Confirmation_controller.rb
class ConfirmationController < ApplicationController
def new
#confirmation = Invitation.find_by_invite_token(params[:invite_token])
end
def confirm
if #confirmation.update_attribute(:accepted, true)
flash[:success] = "Invitation confirmed!"
redirect_to 'static_pages/home'
else
flash[:notice] = "Failed :("
redirect_to 'static_pages/home'
end
end
end
routes.rb
match '/confirmation/:invite_token', to: 'confirmation#new'
match '/confirmation/:invite_token/confirm', to: 'confirmation#confirm'
app/views/confirmation/new.html.erb
Click here to accept:
<%= link_to "Confirm", :controller => "confirmation", :action => "confirm" %>
You need to get your Invitation in the confirm method too.
If you want rails to raise an exception if no invitation was found
def confirm
#confirmation = Invitation.find_by_invite_token!(params[:invite_token])
#confirmation.update_...
end
No exception will be raise. You may want to check manually with a condition in the following case.
def confirm
#confirmation = Invitation.find_by_invite_token(params[:invite_token])
if #confirmation
#confirmation.update_...
else
# do something
end
end
You should find confirmation record before calling update_attribute on it, like you did it in new action:
#confirmation = Invitation.find_by_invite_token(params[:invite_token])
Or, to throw exception when the record is not found and to render 404 page to the user:
#ocnfirmation = Invitation.find_by_invite_token!(params[:invite_token])
The problem is that you never told the program what #confirmation is. What you should do is find it first then run the update. Note this is different from the different answers, just thought I would throw in some variety.
def confirm
# You're missing this line below. Basic search for the confirmation.
# Note too that you will have to pass in the parameter `invite_token` for it to work
# I'm also assuming invite_token is unique among each invitation
confirmation = Invitation.where(invite_token: params[:invite_token])
# Notice that I'm first checking to see if the confirmation record exists, then doing an update
if confirmation and confirmation.update_attribute(:accepted, true)
flash[:success] = "Invitation confirmed!"
redirect_to 'static_pages/home'
else
flash[:notice] = "Failed :("
redirect_to 'static_pages/home'
end
end

Send User email after creating a post, with Action Mailer

I have a User model (with Devise), and a Post model that belongs to the user. I used this railscast (pro) to send the User an email after creating an account
I created a "NewPostMailer"
This is my mailer:
class NewPostMailer < ActionMailer::Base
default :from => "email#gmail.com"
def new_post_email(user)
#user = user
#url = "http://localhost.com:3000/users/login"
mail(:to => user.email, :subject => "New Post")
end
end
My posts_controller:
def create
#post= Post.new(params[:post])
respond_to do |format|
if #deal.save
NewPostMailer.new_post_confirmation(#user).deliver
format.html { redirect_to #post, notice: 'Post was successfully created.' }
post.rb
after_create :send_new_post_email
private
def send_new_post_email
NewPostMailer.new_post_email(self).deliver
end
What do I have to change to send the User an email after he creates a Post. Thanks.
Create another mailer (http://railscasts.com/episodes/206-action-mailer-in-rails-3)
class YourMailerName < ActionMailer::Base
default :from => "you#example.com"
def post_email(user)
mail(:to => "#{user.name} <#{user.email}>", :subject => "Registered")
end
end
In your Post model
after_create :send_email
def send_email
YourMailerName.post_email(self.user).deliver
end
Sending an email is very slow so think about putting this in a background job.
You should be able to use a fairly similar method to do this. First, create an after_create callback in your Post model, with something like:
after_create :send_user_notification
def send_user_notification
UserMailer.post_creation_notification(user).deliver
end
You will need to make sure that there is a relationship between the user and the post, and create the post_creation_notification method in your UserMailer, the same way you made your old one. It might also be worth pointing out that just blindly firing off emails like this isn't necessarily the best way to do this. Not only does it add extra unnecessary time to a request, but it also doesn't fail in a gracefully recoverable fashion. You may wish to explore adding the emails to be sent to a queue (like this, for example) to be processed, with a cron job or something, if the site you are creating will see anything other than very light usage.

How to change email address with Devise on rails3.1

I'd like to have an "edit profile" page, in which the user can change the email address registered when signing up.
I'd like to have the following process:
the user has to input his password to confirm before he makes changes in the email field.
after submitting that page, the user should receive a verification mail just like Devise's default sign up.
the email change is completed as soon as the user clicks the verification token URL on the mail.
How would I do this?
I created this same flow for a site of mine. Here's an example of what you can do:
add to config/routes.rb
(note that the routing could be better, but I did this a while ago)
scope :path => '/users', :controller => 'users' do
match 'verify_email' => :verify_email, :as => 'verify_email'
match 'edit_account_email' => :edit_account_email, :as => 'edit_account_email'
match 'update_account_email' => :update_account_email, :as => 'update_account_email'
end
add to app/controllers/users_controller.rb
def edit_account_email
#user=current_user
end
def update_account_email
#user=current_user
#user.password_not_needed=true
#user.email=params[:address]
if #user.save
flash[:notice]="your login email has been successfully updated."
else
flash[:alert]="oops! we were unable to activate your new login email. #{#user.errors}"
end
redirect_to edit_user_path
end
def verify_email
#user=current_user
#address=params[:address]
UserMailer.confirm_account_email(#user, #address).deliver
end
app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
def confirm_account_email(user, address)
#user = user
#address = address
mail(
:to=>"#{user.name} <#{#address}>",
:from=>"your name <'your_email#domain.com'>",
:subject=>"account email confirmation for #{user.name}"
)
end
end
app/views/user_mailer/confirm_account_email.html.erb
<p>you can confirm that you'd like to use this email address to log in to your account by clicking the link below:</p>
<p><%= link_to('update your email', update_account_email_url(#user, :address=>#address)) %></p>
<p>if you choose not to confirm the new address, your current login email will remain active.

How can I access #user or user from a mailer using Devise and Rails 3?

class JobMailer < ActionMailer::Base
default :from => "emailer#email.com"
def new_job_email_for_client
#
#
#url = "http://simplesite.com/users/login"
mail(:to => #???,
:subject => "You have created a new case on simplesite.")
end
end
I would like each user to receive an email each and every time he/she creates a "job." In other parts of the application, I can access #user and user.email and such, but in the mailer I'm getting "undefined errors."
How can I access the current users email address in the mailer (taking into consideration that Devise is in control of Users)?
I'm not sure if this is a great way of doing it, but this is how I got it working:
def new_job_email_for_client(user_email)
#url = "http://simplesite.com/users/login"
mail(:to => user_email,
:subject => "You have created a new case.")
end

Resources