Rails User Sign Up Mail Confirmation - ruby-on-rails

I'm trying to create a mailer that sends out an email whenever a user signs up. Pretty simple but I'm new to rails.
I have a site that already creates the user. I have a login and sign up page that works correctly, but need some help creating a mailer that sends out an email confirmation link and possibly an option to send out these emails without the user signing up like make a separate page for user invitations.
I've generated a model invitation.rb
class Invitation < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
validates_presence_of :recipient_email
validate :recipient_is_not_registered
validate :sender_has_invitations, :if => :sender
before_create :generate_token
before_create :decrement_sender_count, :if => :sender
private
def recipient_is_not_registered
errors.add :recipient_email, 'is already registered' if User.find_by_email(recipient_email)
end
def sender_has_invitations
unless sender.invitation_limit > 0
errors.add_to_base 'You have reached your limit of invitations to send.'
end
end
def generate_token
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
def decrement_sender_count
sender.decrement! :invitation_limit
end
#attr_accessible :sender_id, :recipient_email, :token, :sent_at
end
and my invitiation_controller.rb
class InvitationsController < ApplicationController
def new
#invitation = Invitation.new
end
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user
if #invitation.save
if logged_in?
Mailer.deliver_invitation(#invitation, signup_url(#invitation.token))
flash[:notice] = "Thank you, invitation sent."
redirect_to projects_url
else
flash[:notice] = "Thank you, we will notify when we are ready."
redirect_to root_url
end
else
render :action => 'new'
end
end
end
What else do I need to edit? how do I hook this up to an already existing user signup and login that is working fine?

You should already have a UsersController or something like that for registration purposes, which you currently access through the signup_url named route. Suppose that this route is now something like:
http://localhost:3000/register/code_here
All you have to do now is check for the invitation in the controller action and process it accordingly like so:
def new
invite = Invite.find_by_token(params[:id]
if invite.nil?
redirect_to root_path, :notice => "Sorry, you need an invite to register"
end
#user = User.new(:email => invite.recipient_email)
end
def create
invite = Invite.find_by_token(params[:token]
if invite.nil?
redirect_to root_path, :notice => "Sorry, you need an invite to register"
end
begin
invite.nil.transaction do
invite.nil.destroy!
#user = User.create(params[:user)
end
redirect_to my_dashboard_path, :notice => "Yay!"
rescue ActiveRecord::RecordInvalid => invalid
render :new, :alert => "Validation errors"
end
end
Without the invite code, you will simply redirect to root page. You may want to DRY that check though. When someone uses the invite code, you may want to delete it from the database. I wrapped it up in a transaction, but this is up to you (creating the user may be more important).
If you want to create a page that allows users to create invitations without signing up, then simply don't add authentication to InvitationsController and update this snippet:
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user if logged_in?
if #invitation.save
Mailer.deliver_invitation(#invitation, signup_url(#invitation.token))
flash[:notice] = "Thank you, invitation sent."
if logged_in?
redirect_to projects_url
else
redirect_to root_url
end
else
render :action => 'new'
end
end
I'm not sure if I covered all the bases, but I think this should point you in the right direction at least.

I can not see where Mailer.deliver_invitation comes from, do you use a gem? would it help if you would create mailer.rb, do you have any error mgs/ stack trace?
Have a look here there are some guides, 5 Action Mailer Configuration
http://guides.rubyonrails.org/action_mailer_basics.html
Consider using devise for user authentication, https://github.com/plataformatec/devise
It is complex, but well documented and easy to configure to jump start.
I assume you are using Rails 3.1 (works also in earlier versions, just find the right guide to your Rails version, to be sure)

Related

Automatically Create Guest User With Rails

I followed Railscast #393 to implement guest users into my application. The only problem I'm having with this approach is that it requires the user to still click a button to create the user (albeit without signing up). My goal is to have this occur automatically, but without happening every time the page is reloaded or visited again. I'm at a loss on how to go about this.
user_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = params[:user] ? User.new(params[:user]) : User.new_guest
if #user.save
current_user.move_to(#user) if current_user && current_user.guest?
session[:user_id] = #user.id
redirect_to root_url
else
render "new"
end
end
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :provider, :uid, :email
has_many :posts, :dependent => :destroy
has_many :comments, :dependent => :destroy
def self.new_guest
new { |u| u.guest = true }
end
def move_to(user)
posts.update_all(user_id: user.id)
comments.update_all(user_id: user.id)
end
end
application.html.erb
<ul>
<li><%= button_to "Try it for free!", users_path, method: :post %></li>
</ul>
I can provide any additional information necessary to help answer this question.
You could potentially handle this in a before_filter, but you'd want to consider that you'd be creating a new guest user any time that anyone or anything (Google bot, for instance) requests a page from your site without an existing session. Requiring some sort of user action to kick it off is probably a good thing. That being said, if you really wanted to do it you could do something like this in your ApplicationController:
before_filter :create_guest_if_needed
def create_guest_if_needed
return if session[:user_id] # already logged in, don't need to create another one
#user = User.new_guest
#user.save
session[:user_id] = #user.id
# do anything else you need here...
end

Rails Authlogic: Login using Database ID

I'm brand new to rails (3.2) and I'm looking for a way to allow users to login using their Database ID and password. Reading the documentation, Authlogic seems to allow login using a login name or email. Is it possible to somehow extend the gem such to allow a user to authenticate by providing their database ID and password?
Here is my current user model
class User < ActiveRecord::Base
attr_accessible :password, :password_confirmation, :current_login_ip, :email, :failed_login_count, :first_name, :last_login_ip, :last_name, :last_request_at, :login_count, :password_salt,
:perishable_token, :persistence_token, :single_access_token
acts_as_authentic
end
And my current User Session model
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Successfully logged in."
redirect_to root_url
else
render :action => 'new'
end
end
def destroy
if UserSession.find
UserSession.find.destroy
flash[:notice] = "Successfully logged out."
end
redirect_to root_url
end
end
My current setup uses email address authentication.
Thanks in advance!
Authlogic allows you to choose the column to use as the login field. See the Login::Config module (login_field option):
acts_as_authentic do |c|
c.login_field = :database_id_or_whatever_fits_your_needs
end

How to permit creator to destroy his own record with Devise on Rails3

When the user is logged in, only the user who create the record can destroy his own record.
What should I add to the code below??
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
flash[:notice] = "topic deleted!"
end
What you are looking for is not really devise but a authorization solution like CanCan.
Devise can only authenticate users and verify that they are logged in and active. What you need is a way to determine if the user has the right to delete this topic or not.
You can of course roll your own like this:
def destroy
#topic = Topic.find(params[:id])
if #topic.user_id == current_user.id
#topic.destroy
flash[:notice] = "topic deleted!"
else
flash[:error] = "not allowed"
end
end
(The code assumes you have a belongs_to :creator, :class_name => :user association set up in your Topic.. But you get the idea).
But using something like CanCan will make your life a whole lot easier and would reduce the code to something like this:
def destroy
#topic = Topic.find(params[:id])
authorize! :destroy, #topic
#topic.destroy
flash[:notice] = "topic deleted!"
end
With your ability file (See defining abilities) set up like this:
can :manage, Topic, :owner_id => user.id

Devise restrict sign up to Admin

I am working on a Rails App that uses Devise as the authentication module, however I want to customize it so that CanCan will only permit Administrators to create a new user. I am having a hard time understanding how to customize the controller for Devise so that this can be done. Any help would be appreciated.
You don't need to customize anything :D
Remove :registerable from your Devise model.
Create your Users CRUD* (just scaffold users)
Use CanCan for user permission on your Users
Controller.
*Check Devise's wiki on how to create a Users CRUD, there is a routing trick you need to do
You can create a "User" controller that will manage users and then simply set permissions for it. So in your new User controller you can have something like:
class UserController < ApplicationController
load_and_authorize_resource
def index
end
def new
end
def show
end
def create
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def edit
end
def update
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated User."
redirect_to user_index_path
else
render :action => 'edit'
end
end
def destroy
if #user.destroy
flash[:notice] = "Successfully deleted User."
redirect_to user_index_path
end
end
end
Assuming you have administrators set to:
can :manage, :all
You should be good to go.
In your routes file you'll need to set up your routes:
resources :user, :controller => "user"
Hope this helps!

Rails3 invitations and mailer - how to add variables to routes/urls?

This should be simple fix, but I've been unable to find an answer. Can anyone point me in the right direction?
I'm implementing a Rails3 beta invite system a la Ryan Bates - http://railscasts.com/episodes/124-beta-invitations
I've set up the mailer to send out the invite urls. Everything works fine, apart from one small issue.
The url generated by the mailer is /user/sign_up.token.
I need to generate /user/sign_up/token (slash instead of period).
I guess I need to change the syntax in "Mailer.invitation().deliver", but I can't find any documentation to help. Can anyone point me in the right direction?
The relevant bits of my routes file:
devise_for :users, :path_prefix => 'registration', :controllers => {:registrations => 'users/registrations'} do
get "registration/users/sign_up/:invitation_token" => "users/registrations#new"
end
Invitations controller:
class InvitationsController < ApplicationController
def new
#invitation = Invitation.new
#title = "Invite a friend"
end
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user
if #invitation.save
if user_signed_in?
Mailer.invitation(#invitation, new_user_registration_path(#invitation.token)).deliver
redirect_to root_url, :notice => "Thank you, your friend will receive their invitation soon."
else
redirect_to root_url, :notice => "Thank you, we'll let you know when the next batch of invites are availale."
end
else
if current_user.invitation_limit > 0
render :action => 'new', :alert => "Sorry, there was a problem! Please try a again."
else
redirect_to root_url, :alert => "Sorry, you don't have any invitations left. Please wait until we issue more."
end
end
end
end
Mailer:
class Mailer < ActionMailer::Base
def invitation(invitation, sign_up)
subject 'Invitation'
recipients invitation.recipient_email
#greeting = "Hi"
#invitation = invitation
#signup_url = sign_up
#sender = invitation.sender_id
invitation.update_attribute(:send_at, Time.now)
end
end
Thank you for any ideas!
Not completely sure if this would work, but maybe try
new_user_registration_url(#invitation.token)
instead of new_user_registration_path.
Another (but not a very good) method would be
new_user_registration_url+"/#{#invitation.token}" #substitute path for url maybe
Hope this helps!
Change your route to
get "registration/users/sign_up/:id" => "users/registrations#new"
and add this to your Invitation model:
def to_param
"#{token}"
end
Then you can simply use
new_user_registration_url(#invitation)

Resources