I have a ruby on rails app. I followed the facebook instructions to add a pixel.
However, to track a conversion, facebook requires you to put the page in the conversion that will appear when the desired result is reached. i.e if I want to show a customer has signed up, I would put the page you go to after signing up as the success object to track.
My issue is that in my application when a customer signs up, there is no landing page. The app takes the user back to the homepage. It shows a message on the homepage so I am trying to see if there is a way to track conversions from controller actions instead of actual pages.
The actions I need to count don't have pages, they are controller actions. Is there a gem, documentation, or a best practice on how to do this that anyone is aware of?
Here is the pixel that goes into the layouts file...
<!-- Facebook Pixel Code -->
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '18027724*****', {
em: 'insert_email_variable,'
});
fbq('track', 'PageView');
</script>
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=180277241*****&ev=PageView&noscript=1"
/></noscript>
<!-- DO NOT MODIFY -->
<!-- End Facebook Pixel Code -->
However I need it to fire here in the controller...
def create
#reportapproval = current_user.reportapprovals.create(authorization_params)
if #reportapproval.valid?
if #reportapproval.manager_paying_report == true
flash[:notice] = "Please make payment before proceeding"
redirect_to new_managers_charge_path(id: #reportapproval.id)
else
flash[:notice] = "You have requested a report from #{#reportapproval.tenant_last_name}."
redirect_to managers_dashboard_path
#reportapproval.save
end
<----PIXEL FIRES HERE IN THE PROCESS ----->
else
flash[:error] = #reportapproval.errors.full_messages.to_sentence
redirect_to managers_dashboard_path
end
end
A cleaner solution might be to use the rack-tracker gem found here:
https://github.com/railslove/rack-tracker
The master-branch doesn't yet have the new facebook pixel, so use this fork instead:
https://github.com/jolim/rack-tracker
Add this to your gemfile:
gem 'rack-tracker', git: 'https://github.com/jolim/rack-tracker.git', ref: 'c2b1b30'
In your application.rb:
config.middleware.use(Rack::Tracker) do
handler :facebook, { id: 'PIXEL_ID' }
end
Where you replace PIXEL_ID with your facebook pixel id.
Then in your controller:
def create
...
# PIXEL FIRES HERE IN THE PROCESS
tracker do |t|
t.facebook :track, { type: 'Purchase', options: { value: 100, currency: 'USD' } }
end
...
end
You can add a helper to check the current controller and action, then wrap displaying the tracking pixel in that. It would look something like:
# In ApplicationHelper
def display_tracking_pixel?(controller)
controller.current_controller == "my_controller" && controller.current_action == "my_action"
end
# In the layout
<% if display_tracking_pixel?(controller) %>
<!-- Tracking embed goes here -->
<% end %>
Related
I've just upgraded an app from Rails 3 to Rails 4, and I'm seeing a bunch of InvalidAuthenticityToken exceptions popping up. Digging in it looks like it is fairly common for our users to have multiple long-lived tabs open on our site. So I think what's happening is something like this: user Alice has three tabs open and her session expires. She logs back in on one of the tabs, which updates the authenticity token stored in her session. Then she returns to one of the other open tabs and tries to submit data, but she gets a 500 error from the InvalidAuthenticityToken error we raised.
It would clearly be nice to do some error handling for Alice so she doesn't get a 500 error. I'm wondering about best practices for this kind of situation. What would be a nice way to handle Alice's submission from the expired tab? I don't want to expire the current session, because that would be super annoying from the user's perspective ("I just logged in, you dolt!"). Ideally, I just want the user to reload the page, which would result in the correct authenticity token being present in the form. Or should I be doing something different so that the long-lived tabs that are open notice that the session has expired and force a reload? This would probably be sub-optimal from the user's point of view, because they liked having that page ready and easily accessible to keep referencing, which is why they left it open in the tab in the first place.
In my rails 4.1.1 app I had the same problem. I solved it by adding this code to ApplicationController. I found this solution here.
rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_referer_or_path
def redirect_to_referer_or_path
flash[:notice] = "Please try again."
redirect_to request.referer
end
This way any controller that inherits from ApplicationController will handle the error with a redirect to the page the form was submitted from with a flash message to give the user some indication of what went wrong. Note this uses the hash syntax introduced in Ruby 1.9. For older versions of Ruby you will need to use :with => :redirect_to_referer_or_path
The solution to this problem can be divided into 2 phases. Phase 1 addresses the issue of ActionController::InvalidAuthenticityToken error and phase 2 deals with the issue of long tabs waiting idly.
Phase 1(1st variation)
One way to go about is redirect the user back to their location before the error. For ex. if Alice has 3 tabs open, the first one expires and Alice logs in again in it because she was browsing on it. But when she moves to tab 3 which has URL 'http://example.com/ex' and submits a form. Now instead of displaying her an error we can redirect her back to 'http://example.com/ex' with her submitted form values already pre-filled in the form for easy use.
This can be achieved by following this approach:
1) ApplicationController - Add this function:
def handle_unverified_request
flash[:error] = 'Kindly retry.' # show this error in your layout
referrer_url = URI.parse(request.referrer) rescue URI.parse(some_default_url)
# need to have a default in case referrer is not given
# append the query string to the referrer url
referrer_url.query = Rack::Utils.parse_nested_query('').
merge(params[params.keys[2]]). # this may be different for you
to_query
# redirect to the referrer url with the modified query string
redirect_to referrer_url.to_s
end
2) You need to include a default value for all your form fields. It will be the name of that field.
...
<% f.text_field, name: 'email', placeholder: 'Email', value: params[:email] %>
...
This way whenever Alice will submit a form with wrong authenticity_token she will be redirected back to her form with the original values she submitted and she will be shown a flash message that kindly retry your request.
Phase 1(2nd variation)
Another way to go about is just redirect Alice back to the form which she submitted without any pre-filled values.
This approach can be achieved by:
1) ApplicationController - Add this function:
def handle_unverified_request
flash[:error] = 'Kindly retry.'
redirect_to :back
end
Phase 2
To tackle the problem of long awaited tabs you can take the help of SSEs. Rails 4 has ActionController::Live for handling SSEs.
1) Add this to any controller:
include ActionController::Live
...
def sse
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, retry: 2000, event: 'refresh') # change the time interval to your suiting
if user_signed_in? # check if user is signed-in or not
sse.write('none')
else
sse.write('refresh')
end
ensure
sse.close
end
2) Give the above function a GET route in your routes file. Lets call this route '/sse'
3) Add this in your layout:
<% if user_signed_in? %> # check if user is signed-in or not
<script>
var evtSource = new EventSource("/sse");
evtSource.addEventListener('refresh', function(e){
if(e.data == 'refresh'){
window.location = window.location.href;
}
});
</script>
<% end %>
Note: using EventSource is not supported by all browsers. Please check out the Browser compatibility section.
Source:
rails 4 redirect back with new params & MDN: Using server-sent events
Answer by Durrell is perfectly alright, I am just providing an alternative way to write the same thing. This needs to go in ApplicationController.
rescue_from ActionController::InvalidAuthenticityToken do |_exception|
flash[:alert] = 'Please try again.'
redirect_back fallback_location: root_path
end
I have a page that lets one create records - if the validations aren't satisfied, it redirects to the same page and shows an error message. Here's that snip from the controller:
def create
#signature = Signature.new(signature_params)
if #signature.save
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
end
The trouble is, this is resulting in a full page refresh - so the error message isn't visible because the input form is placed under the fold of the page. I can place it at the top of the page, of course, but is there a way to show the message without reloading the page? Thanks.
OK, so here's what I've settled on:
1) I'm handling validation on the client side with HTML5 "required" attributes - they were created for this explicit purpose and no other gems or plugins are needed. They are supported in all major browsers. Details in this article.
2) I've moved the error messages to the top of the page to handle the case in which a user either is on an old or mobile browser or has JavaScript disabled. Error messages must work with a complete request-response cycle (even if this means re-loading the page) before they work with anything else - this is the unobtrusive JavaScript approach.
3) For the AJAX version, I'm going to be using remote: => true on the form element as explained in the Rails guides. I might be making this open source once I'm done with the callback part of it, and will post a link here.
Obviously, handling errors with flash is the most uniform & DRY way to show the user what's going on, but if you're willing to think outside the box, you'll be able to use Ajax to accomplish a similar job by just handling the errors yourself:
Code Example
#app/controllers/signatures_controller.rb
def create
#signature = Signature.new(signature_params)
if #signature.save
#success = "true"
end
respond_to do |format|
format.js { #errors = error_messages(#signature) }
format.html {
if #success.defined?
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
}
end
end
#app/views/signatures/create.js.erb
<% unless #success.defined? %>
alert(<%=j #errors.inspect() %>)
<% end %>
#app/assets/javascripts/signatures.js
$(document).on("submit", "#signature_form", function() {
$.ajax({
url: "/signatures"
type: "POST"
data: $(this).parent().serialize(); //serialize the form (not the button)
error: function() {
alert("Sorry, there was an error!");
}
});
});
You'd actually be better using JSON for this. If you like the idea, I can refactor it to include JSON for you!
I have a rails app that imports all your Facebook contacts. This takes some time. I would like to be able to show a "please wait" page while the importing keeps happening in the back.
It seems that I cannot put render and redirect_to on the same action in the controller. How can I do this?
if #not_first_time
Authentication.delay.update_contact_list(current_user)
else
render 'some page telling the user to wait'
Authentication.import_contact_list(current_user)
end
redirect_to :root_path, :notice => 'Succesfully logged in'
If it is the users first time in the site, i want to render a "please wait page", start importing, and once its done redirect to the root path, where a lot of processing with this data happens
If it is not the first time, then put the contact update in the background (using the delayed_jobs gem) and go straight to the home page
I'm using the fb_graph gem to import the contacts. Here's the method
def self.import_contact_list(user)
user.facebook.friends.each do |contact|
contact_hash = { 'provider' => 'facebook', 'uid' => contact.identifier, 'name' => contact.name, 'image' => contact.picture(size='large') }
unless new_contact = Authentication.find_from_hash(contact_hash)
##create the new contact
new_contact = Authentication.create_contact_from_hash(contact_hash)
end
unless relationship = Relationship.find_from_hash(user, new_contact)
#create the relationship if it is inexistent
relationship = Relationship.create_from_hash(user, new_contact)
end
end
end
Edit
I added the solution suggested below, it works!
Here's is my 'wait while we import contacts' view from the action "wait"
<script>
jQuery(document).ready(function() {
$.get( "/import_contacts", function(data) {
window.location.replace("/")
});
});
</script>
<% title 'importing your contacts' %>
<h1>Please wait while we import your contacts</h1>
<%= image_tag('images/saving.gif') %>
Thanks!
A single request receives a single response - you can't render content and redirect.
If I were you I would always do the lengthy process in delayed job - tying up passenger/unicorn instances is never a great idea. Render a 'please wait page' that refreshes periodically to check whether the delayed job has completed (if you stash the id of the delayed job you can test whether it is still in the db. When te job completes it will be deleted). When the job is complete, redirect to the results page. You could also do the periodic checking bit via ajax.
I'm trying to integrate Devise into my application. I need implement login form at top of the page (I've implemented this form into layout page) and I've implemented registration which contains registration form.
But it shows validation errors for both form when I tried submit incorrect registration data.
Without more information, it's hard to guess what the problem is. I've found the Wiki pages to be really helpful (and increasingly so), though you may have already looked them over:
Devise Wiki Pages
Two pages that might be relevant to your needs:
Display a custom sign_in form anywhere in your app
Create custom layouts
Hope this helps!
-- ff
The problem of seeing the validation errors for both forms stems from the 2 things. First, devise forms use a generic 'resource' helper. This creates a User object, and that same user objet gets used for both the sign up and the sign in form. Second, devise errors are typically displayed using the 'devise_error_messages!' helper which uses that same shared resource.
To have sign in and sign up on the same page you need to create different user objects for each form, and a new way of displaying the error messages.
First off, you'll need to create your own registration controller (in app/controllers/users/)
class Users::RegistrationsController < Devise::RegistrationsController
include DevisePermittedParameters
protected
def build_resource(hash=nil)
super
# Create an instance var to use just for the sign up form
#sign_up_user = self.resource
end
end
And update your routes file accordingly
devise_for :users, controllers: {
registrations: 'users/registrations'
}
Next you'll need your own error messages and resource helpers. Create a new helper like devise_single_page_helper.rb and add the following:
module DeviseSinglePageHelper
def devise_error_messages_for_same_page(given_resource)
return "" if given_resource.errors.empty?
messages = given_resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
sentence = I18n.t("errors.messages.not_saved",
count: given_resource.errors.count,
resource: given_resource.class.model_name.human.downcase)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
def sign_up_user
#sign_up_user ||= User.new(username: 'su')
end
def sign_in_user
#sign_in_user ||= User.new(username: 'si')
end
end
Finally, in your views, update your forms like so:
-# The sign up form
= simple_form_for(sign_up_user, url: registration_path(resource_name)) do |f|
-#...
= devise_error_messages_for_same_page(sign_up_user)
-# The sign in form
= simple_form_for(sign_in_user, url: sessions_path(resource_name)) do |f|
#...
= devise_error_messages_for_same_page(sign_in_user)
All of this together gives you 2 different objects - 1 for sign up and 1 for sign in. This will prevent the error messages from one showing in the other. Please note that recommend putting both forms on your sign in page (and perhaps having the default sign up page redirect to the sign in page) because by default a failed sign in attempt will redirect to the sign in page.
You should have two forms on the page — one for signing up and one for registering. If you want a single form and multiple potential actions you are going to need a couple buttons that get handled client side and change the form's action & method to the appropriate route depending you want to create a user or a session.
If you think you did this already, the problem almost certainly lies in your code. If you were to share it with us we could perhaps point out something you may have missed.
Think about a simple Rails scaffold application with a "new" action containing a form to add records to a database with a "save" button. After the "create" action the controller redirects to the "show" action, where the user can use the "edit" link to edit the just inserted record. So far, so simple.
But if the user instead uses the browser's back button after creating a record to get back to the "new" action, the browser shows the form with the values the user just has entered. Now he changes some values and presses "save" again. He thinks that this would change the record, but of course this creates a new record.
What is the preferred way to prevent such duplicate entries? I'm looking for a general solution, maybe based on cookies or JavaScript.
After some investigations I found a suitable solution based on cookies. Here it is:
In the controller's "new" action, a timestamp with the current time is generated and rendered in the form as hidden field. When the user submits the form, this timestamps gets back to the controller's "create" action. After creating the record, this timestamp is stored in the session cookie. If the user goes back to the "new" form via browser's back button, he gets a stale form, which means its timestamp is older than the one stored in the cookie. This is checked before creating the record and results in an error message.
Here is the controller code:
def new
#post = Post.new
#stale_form_check_timestamp = Time.now.to_i
end
def create
#post = Post.new(params[:post])
if session[:last_created_at].to_i > params[:timestamp].to_i
flash[:error] = 'This form is stale!'
render 'new'
else
#post.save!
#stale_form_check_timestamp = Time.now.to_i
session[:last_created_at] = #stale_form_check_timestamp
end
end
And here the form code:
- form_for #post do |f|
= tag :input, :type => 'hidden', :name => 'timestamp', :value => #stale_form_check_timestamp
= f.input :some_field
= .......
When I had that same problem I created this little gem that solves it. When the user hits back, he's redirected to the edit_path of the record, instead of going back to the new_path.
https://github.com/yossi-shasho/redirect_on_back
You can do something like:
def create
#user = User.new(params[:user])
if result = #user.save
redirect_on_back_to edit_user_path(#user) # If user hits 'back' he'll be redirected to edit_user_path
redirect_to #user
end
end
Your model validations will ensure things like email addresses are unique, but I think this is more about usability and experience than anything else.
Say you are talking about an account creation form. First of all, your form submit button should say something like "Create Account", instead of just "Submit". Then depending on whether it was successful or not, show a message like either "Account successfully created" or "There were errors creating your account". If the user sees this message, they will know what happened.
Sure you can't prevent someone from hitting the back button and hitting enter again, but you should design for the majority of use cases. If they happen to hit back, they will see the button that says "Create Account". You should probably have some other text on the page that says "Please sign up for a new account to get started".
Just my $0.02.
Session or cookie may result in sides effects.
I totally agree : if there is a way to validate with your model, it's the safest way to prevent duplicate records.
Still you can do 2 things. Prevent browser caching : fields will appear empty in the form when the user clicks on the back button. And disable the "Create" button when clicked.
= f.submit "Create", :disable_with => "Processing..."
When your user will press the back button the button will be disabled.
You can use validators to make sure that no duplicate values are inserted. In this case validates_uniqueness_of :field
If you for example want to prevent users from having the same email address you could put the following code in your user model.
validates_uniqueness_of :email
This checks the column for any previous entries that are the same as the one your trying to inert.
Good luck
base on #Georg Ledermann answer i make this little snip of code for redirect to edit path if the user hits back and then hits create.
#objects_controller.rb
def new
#object = Object.new
#stale_form_check = Time.now.to_i
end
def create
#object = Object.new(object_params)
#function defined in application_controller.rb
redirect_to_on_back_and_create(#object)
end
#application_controller.rb
private
def redirect_to_on_back_and_create(object)
if session[:last_stale].present? and session[:last_stale_id].present? and session[:last_stale].to_i == params[:stale_form_check].to_i
redirect_to edit_polymorphic_path(object.class.find(session[:last_stale_id].to_i)), alert: "Este #{object.model_name.human} ya ha sido creado, puedes editarlo a continuación"
else
if object.save
session[:last_stale] = params[:stale_form_check].to_i
session[:last_stale_id] = object.id
redirect_to object, notice: "#{object.model_name.human} Creado con éxito"
else
render :new
end
end
end
And finally add the #stale_form_check param to your form
<%= hidden_field_tag :stale_form_check, #stale_form_check %>
You could always abstracts this method where you need it, but in this way you could avoid lots of repetition in your project if you need this behavior in many parts
Hope it helps the next one, i used to use redirect_on_back gem, but it didn't work for me this time, the _usec param that this gem uses, was always been reset, so it can't compare in every time when it was need
Here's something that worked for me.
You will need to do 2 things: Create a method in your controller and add a conditional statement in that same controller under your 'create' method.
1) Your method should return the total count of that object from that user.
EX:
def user
current_user.object.count
end
2) Add conditional statement in your 'create' method.
EXAMPLE:
def create
#object = Object.create(object_params)
#object.save if user == 0
redirect_to x_path
end
I hope this helps!
Add html: { autocomplete: "off" } in your form_for like this:
<%= form_for #object, url: xxx_path, html: { autocomplete: "off" } do |f| %>