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
Related
I get this error when I try to sign in a user, and can't figure out why. It's weird because when I run the following code I get the BCrypt Error, however when I change the find_by line (line 7) from can_email (candidate's email) to can_name (candidate's first name) I don't get the error at all, it just doesn't sign in the user presenting an "invalid password/email combination" error message on the webpage regardless if the combination is right or not. It's something to do with the password but I can't pin point it.
class SessionsController < ApplicationController
def new
end
def create
candidate = Candidate.find_by_can_email(params[:can_email])
if candidate && candidate.authenticate(params[:password]) **Error highlights this line**
session[:candidate_id] = candidate.id
redirect_to candidate
else
flash.now[:error]="Invalid email/password combination"
render 'new'
end
end
def destroy
if signed_in?
session[:candidate_id] = nil
else
flash[:notice] = "You need to log in first"
end
redirect_to login_path
end
end
Having the SessionController i am assuming you have a route as follows
# This is just a sample
post 'login' => "sessions#create" # gives login_path
Since there will be no session model i assume you have the form as follows
form_for(:session, url: login_path)
Now if you are collecting eg can_email and password you get
{session: {password: 'foo', can_email: 'foo#bar.com'}}
Accessing params[:session] returns the hash containing email and passowrd
So i think you should obtain them as follows
def create
candidate = Candidate.find_by(can_email: params[:session][:can_email])
if candidate && candidate.authenticate(params[:session][:password])
# login the user
else
# whatever
end
end
I got this error too, but in my case it was the result of myself having changed the encrypted_password value of my user in the database a while back and then forgetting about it.
This was easily fixed just by updating the password :)
I am new to rails and have a task that asks me to send an invitation for any user to be admin in my magazine here is my piece of code
def invite
inviteUser = { 'user_id' => current_user.id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
I need to replace current_user.id with something that refers to any user's id which exists in my database to send him an invitation to be admin with me I tried to add #User=Users.All and then pass it as a variable but it got me an error I tried a lot of things but every time I get an error except for adding current_user.id
ps: I am using devise for authentication
You asked a couple things, and it is kind of confusing what you want to do.
Here is how you get all ids of records in a model.
Rails4: User.ids
Rails3: User.all.map(&:id)
Or (not sure if #pluck is in Rails 3 or not)
User.pluck(:id)
If you want to get a random user (you mentioned "any user") you could do.
User.find(User.pluck(:id).sample)
Though I think what you really want to do is to pass the id or some other attribute of a user as a param to the action and send that user an invitation.
Presumably you either have a post or get route for "users#invite" (the action you wrote in your question). You can add a named parameter there or you can pass a url param or if you are using a post route, you could add the param to the post body.
Then in your contoller you can do something like this (I'll use email as an attribute):
def invite
#user = User.find_by(email: params[:user_email])
#Rails 3 like this
# #user = User.find_by_email(params[:user_email])
# now do stuff with user
end
User.all will return you the collection of users. So,
Find the user object to get an id...
Try this code....
def invite
inviteUser = { 'user_id' => User.find_by_email('user#example.com').id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
You can try
User.last.id
or
User.find_by_email("xyz#test.com").id
or
User.where(email: "xyz#test.com").first.id
Replace xyz#test.com with desired user email address. To get more details on rails active record query interface, please read rails guides http://guides.rubyonrails.org/active_record_querying.html
I am newbie and following tutorials i pust my up application and currently hosting on free heroku dyno. The issue is that some time the form is not getting posted and I got into habit of stopping it using the browser stop button and then posting it again. I have now realized that my email reputation has dropped to 40% , the reason being the same email sent to same address multiple times once i resent the form once it failed or i stopped start and resubmit form. To send email after save i am using the following code in the create action. What I should do to make sure only one email get sent.
def create
#job_post = JobPost.new(params[:job_post])
#job_post.jobuuid = SecureRandom.hex(7)
if #job_post.save
if user_signed_in?
#job_post.update_attribute(:user_id, current_user.id)
else
#job_post.update_attribute(:user_id, 1)
end
# Handle a successful save.
# Email to JobPoster
JobMailer.jobposting_acknowledgment(#job_post).deliver
# Email to Admin
JobMailer.jobposting_adminnotification(#job_post).deliver
flash[:notice] = "Job posting is received and is being manually reviewed."
redirect_to root_path
else
render 'new'
end
end
Move #job_post.jobuuid = SecureRandom.hex(7) to your new action and add a hidden field in your form, so this value is submitted. You can then use it to identify that this #job_post has been already submitted:
def create
redirect_to root_url, alert: 'Job already submitted' if JobPost.exists?(jobuuid: params[:job_post][:jobuuid])
#job_post = JobPost.new(params[:job_post])
#job_post.user_id = user_signed_in? ? current_user.id : 1
begin
#job_post.transaction do
#job_post.user_id = user_signed_in? ? current_user.id : 1
#job_post.save!
if new_job
JobMailer.jobposting_adminnotification(#job_post).deliver
JobMailer.jobposting_acknowledgment(#job_post).deliver
end
end
flash[:notice] = "Job posting is received and is being manually reviewed."
redirect_to root_path
rescue ActiveRecord::RecordInvalid
render 'new'
end
end
Note that you need to ensure that jobuuid is unique and that there is no JobPost with that number in your db. There are 268 millions of 7-digit long hex string, so if you expect extremely heavy traffic there is a potential chance for a conflict (quite small, but still there is).
In my current app, i use Geocoder gem to get the city and the country of the visitor. I use hidden fields in my view to get these details. When the login form is submitted, these details will be sent to the controller and the controller will save them to the database. When I try to get these details directly from the controller by using
request.location.city
It will assigning a blank value to the database. If I use hidden fields in the view, some one can temper with them right? So, how can I fix this?
You should store visitor information before you render any content:
class UsersController
def new
# I suspect that, for fast insert, you should probably use a NoSQL database
# to perform `store!` or even just write it to a log file
Visitor.store!(:city => request.location.city, :ip => request.ip)
end
def create
#user = User.build(params[:user].merge(:city => request.location.city))
if #user.valid?
#user.save
flash[:notice] = "You've been registered!"
redirect_to user_dashboard_path
else
flash[:notice] = "Couldn't register your account"
render action: "new"
end
end
end
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