I'm using Devise invitable for invitation. Typically, in the invitation email there will be a link to redirect the invitee to the sign_in page, some url like this
mywebsite.com/users/invitation/accept?invitation_token=J-azZ8fKtkuAyp2VZWQX
This url comes from invitation_instructions.html:
<p><%= link_to 'Accept invitation', accept_invitation_url(#resource, :invitation_token => #token) %></p>
Now I want to return the invitation url in my controller as json response, something like this:
def invite
invitee = User.invite!({:email => email}, current_user)
accept_invitation_url = ....
render :json => accept_invitation_url
end
any idea how to get the accept_invitation_url in the controller? Thanks!
try to include the url helpers module in your controller:
class MyController < ApplicationController
include DeviseInvitable::Controllers::UrlHelpers
def invite
invitee = User.invite!({:email => email}, current_user)
render :json => accept_invitation_url(invitee, :invitation_token => invitee.token)
end
end
The URL Helper module for the Devise Invitable Gem can be found here on github
Ok the raw invitation token is not accessible by default because it's a instance variable without accessor (source), there are two ways you could solve this.
The ugly way, without modifying your model class:
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.instance_variable_get(:#raw_invitation_token)
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
The clean way, by adding an attribute reader to your user model class:
# User Model
class User < ActiveRecord::Base
attr_reader :raw_invitation_token
# rest of the code
end
# In your controller
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.raw_invitation_token
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
Update (16th October 2015):
It seems like the UrlHelper module has been removed and the invitation is handled as a normal route, so you can remove the include DeviseInvitable::Controllers::UrlHelpers and replace the accept_invitation_url call with:
Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token)
I found out that to use accept_invitation_url outside the standard invitation mailer view you need to include inside the mailer the following helper:
include Devise::Controllers::UrlHelpers
I tried Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token) but it is not working.
Related
Is it possible limit the number of emials sent for devise recoverable in a period of time for each user? How can I defend against malicious requests?
I would use grecaptcha to protect your form where you let the user rescue his account.
It's really easy and simple to use and include it in your rails app.
In your view:
<%= form_for #user do |f| %>
<%= recaptcha_tags %>
<% end %>
In your controller, create action:
def create
verify_recaptcha(model: #user) # => returns true or false
end
To limit: "emials sent for devise recoverable"
Example Gemfile:
gem 'simple_captcha2'
routes:
devise_for :users, :controllers => { ..., :passwords => 'passwords', ... }
app/controllers/passwords_controller.rb:
class PasswordsController < Devise::PasswordsController
prepend_before_action :require_no_authentication
#
# GET /resource/password/new
def create
if simple_captcha_valid?
super
else
....
end
end
end
app/views/devise/passwords/new.html.erb
into the form_for:
<%= show_simple_captcha %>
I am trying to pass a hidden field from a form whose value is derived from a text blob that user can edit on the webpage. (I use bootstrap-editable to let the user edit the blurb by clicking on it)
Here is the actual workflow:
User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
If the user want they can click on the text and edit it. This will make a post call via javascript to update_email method in Invitation controller
After the text is updated user is redirected back so now the user sees the same page with updated text. This works and user sees the updated text blurb instead of default [1-3] can happen any number of times
When the user submits the form , I expect to get the final version of email that I can save in the db and also trigger an email invitation to the users friend
Problem:
I keep getting default text from form parameters. Any idea what I am doing wrong?
Here is the form (Its haml instead of html)
#new-form
= form_for #invitation, :url=> invitations_path(), :html => {:class => 'form-inline', :role => 'form'} do |f|
.form-group
= f.text_field :email, :type=> 'email', :placeholder=> 'Invite your friends via email', :class=> 'form-control invitation-email'
= f.hidden_field :mail_text, :value => #invitation_email
= f.submit :class => 'btn btn-primary submit-email', :value => 'Send'
Here is the invitation controller:
class InvitationsController < ApplicationController
authorize_resource
before_filter :load_invitations, only: [:new, :index]
before_filter :new_invitation, only: [:new, :index]
before_filter :default_email, only: [:index]
#helper_method :default_email
def create
Invitation.create!(email: params[:invitation][:email], invited_by: current_user.id, state: 'sent', mail_text: params[:invitation][:mail_text], url: {referrer_name: current_user.name}.to_param)
redirect_to :back
end
def update_email
#invitation_email = params[:value]
flash[:updated_invitation_email] = params[:value]
redirect_to :back
end
private
def invitation_params
params.require(:invitation).permit!
end
def load_invitations
#invitations ||= current_user.sent_invitations
end
def new_invitation
#invitation = Invitation.new
end
def default_email
default_text = "default text"
#invitation_email = flash[:updated_invitation_email].blank? ? default_text : flash[:updated_invitation_email]
end
end
Assuming you are using Rails 4 then you need to permit the mail_text parameter:
class InvitationsController < ApplicationController
# ...
private
def invitation_params
params.require(:invitation).permit(:email, :mail_text) #...
end
end
Depending on your settings rails strong parameters will either raise an error or just silently null un-permitted params.
I have to say that your flow is a bit weird and that it may be better if you actually use a
more RESTful pattern:
1. User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
Send a AJAX POST request to /invitations (InvitationsController#create) it should return a JSON representation of the UNSENT invitation, store the returned invitation id on the form.
Note that you may need to setup the validations on your Invitation model so that it allows :email and :mail_text to be blank on creation
class Invitation < ActiveRecord::Base
validates :email, allow_blank: true
# ...
# Do full validation only when mail is being sent.
with_options if: :is_being_sent? do |invitation|
invitation.validates :email #...
invitation.validates :mail_text #...
end
# ...
def is_being_sent?
changed.include?("state") && state == 'sent'
end
end
2. User edits text
Send a AJAX PUT or PATCH request to /invitations/:id and update the invitation.
3. User clicks send
Send a POST request to /invitations/:id/send. Update the state attribute and validate.
If valid send invitation. Display a message to user.
class InvitationsController < ApplicationController
# ...
# POST /invitations/:id/send
def send
#invitation = Invitation.find(params[:id])
# Ensure we have latest values from form and trigger a more stringent validation
#invitation.update(params.merge({ state: :sent })
if #invitation.valid?
#mail = Invitation.send!
if #mail.delivered?
# display success response
else
# display error
end
else # record is invalid
# redirect to edit
end
end
# ...
end
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!
I have a custom mailer (UserMailer.rb) and a few methods to override the default Devise methods for the welcome email and forgot password emails. The mailer uses a custom template to style the emails--and it works great.
In config/initializers, I have a file with
module Devise::Models::Confirmable
# Override Devise's own method. This one is called only on user creation, not on subsequent address modifications.
def send_on_create_confirmation_instructions
UserMailer.welcome_email(self).deliver
end
...
end
(Again, UserMailer is setup and works great for the welcome email and reset password email.)
But what's not working is the option to "Resend confirmation instructions." It sends with the default Devise styling and I want it to use the styling of my mailer layout. I know I can manually add the layout to the default Devise layout, but I'd like to keep DRY in effect and not have to do that.
I've tried overriding the send_confirmation_instructions method found here, but I'm getting a wrong number of arguments (1 for 0) error in create(gem) devise-2.2.3/app/controllers/devise/confirmations_controller.rb at
7 # POST /resource/confirmation
8 def create
9 self.resource = resource_class.send_confirmation_instructions(resource_params)
In my initializer file, I'm able to get to this error by adding a new override for Devise, but I'm probably not doing this correctly:
module Devise::Models::Confirmable::ClassMethods
def send_confirmation_instructions
UserMailer.send_confirmation_instructions(self).deliver
end
end
Any ideas?
You don't have to go through that initializer to do that. I've done this by overriding the confirmations controller. My routes for devise look like:
devise_for :user, :path => '', :path_names => { :sign_in => 'login', :sign_out => 'logout', :sign_up => 'signup'},
:controllers => {
:sessions => "sessions",
:registrations => "registrations",
:confirmations => "confirmations"
}
Then, create the confirmations_controller and extend the Devise::ConfirmationsController to override:
class ConfirmationsController < Devise::ConfirmationsController
In that controller, I have a create method to override the default:
def create
#user = User.where(:email => params[:user][:email]).first
if #user && #user.confirmed_at.nil?
UserMailer.confirmation_instructions(#user).deliver
flash[:notice] = "Set a notice if you want"
redirect_to root_url
else
# ... error messaging or actions here
end
end
Obviously, in UserMailer you can specify the html/text templates that will be used to display the confirmation message. confirmation_token should be a part of the #user model, you can use that to create the URL with the correct token:
<%= link_to 'Confirm your account', confirmation_url(#user, :confirmation_token => #user.confirmation_token) %>
I want to integrate Paypal within the Devise user registration process. What I want is to have a standard rails form based on the devise resource, that also has custom fields belonging to the user's model.
When a user fills in those fields and clicks on signup, it will be redirected to Paypal, when he clears from paypal and returns to our site then the user data must be created.
For the scenario where the user fill's out the paypal form but doesn't come back to our site, we have to keep record of user before redirecting to Paypal.
For this we can create a flag in user model and use Paypal IPN and when the user transaction notified, set that flag.
But in the case when the user is redirected to Paypal but doesn't complete the transaction, if the user returns to registration and signup again, our model should not throw error saying that the email entered already exists in the table.
How can we handle all these scenarios, is there any gem or plugin available to work with?
Here i am posting the detail code for performing the whole process.
registration_controller.rb
module Auth
class RegistrationController < Devise::RegistrationsController
include Auth::RegistrationHelper
def create
#user = User.new params[:user]
if #user.valid?
redirect_to get_subscribe_url(#user, request)
else
super
end
end
end
end
registration_helper.rb
module Auth::RegistrationHelper
def get_subscribe_url(user, request)
url = Rails.env == "production" ? "https://www.paypal.com/cgi-bin/webscr/?" : "https://www.sandbox.paypal.com/cgi-bin/webscr/?"
url + {
:ip => request.remote_ip,
:cmd => '_s-xclick',
:hosted_button_id => (Rails.env == "production" ? "ID_FOR_BUTTON" : "ID_FOR_BUTTON"),
:return_url => root_url,
:cancel_return_url => root_url,
:notify_url => payment_notifications_create_url,
:allow_note => true,
:custom => Base64.encode64("#{user.email}|#{user.organization_type_id}|#{user.password}")
}.to_query
end
end
payment_notification_controller.rb
class PaymentNotificationsController < ApplicationController
protect_from_forgery :except => [:create]
layout "single_column", :only => :show
def create
#notification = PaymentNotification.new
#notification.transaction_id = params[:ipn_track_id]
#notification.params = params
#notification.status = "paid"
#custom = Base64.decode64(params[:custom])
#custom = #custom.split("|")
#user = User.new
#user.email = #custom[0]
#user.organization_type_id = #custom[1].to_i
#user.password = #custom[2]
if #user.valid?
#user.save
#notification.user = #user
#notification.save
#user.send_confirmation_instructions
end
render :nothing => true
end
def show
end
end