Button_to in email not posting - ruby-on-rails

SEE UPDATE for evolution of this question
On my website, each user has a dashboard where s/he can click a link to either ACCEPT or DECLINE an request. Depending on what is clicked, the Request record is then PATCHed with the relevant status. To make it easier for users, I'm trying to embed this dashboard in an email to them so that they never have to go to the website directly; think of an email that looks like this:
Hi there,
You have the following requests, click ACCEPT/DECLINE next to the request to do so
Request A: ACCEPT, DECLINE
Request B: ACCEPT, DECLINE
....
The only way to make this work thus far has been to have a parallel set of GET routes for the links used in email, versus the PATCH routes for the links used in the actual website dashboard.
Wondering if there's a better way of doing this?
Routes
patch 'inventories/:id/accept', to: 'inventories#accept', as: 'lender_accept'
patch 'inventories/:id/decline', to: 'inventories#decline', as: 'lender_decline'
get 'inventories/:id/accept_email', to: 'inventories#accept', as: 'lender_accept_email'
get 'inventories/:id/decline_email', to: 'inventories#decline', as: 'lender_decline_email'
Link in email
<%= "#{link_to 'ACCEPT', lender_accept_email_url(borrow), method: :patch} or #{link_to 'DECLINE', lender_decline_email_url(borrow)}" %>
Link on website dashboard
<%= "#{link_to 'ACCEPT', lender_accept_path(borrow), method: :patch} or #{link_to 'DECLINE', lender_decline_path(borrow), method: :patch}" %>
UPDATE
Ok tried the button_to to generate a form to POST to avoid using GET to do POST as was the "patchy" solution above, still not working...
Routes:
post 'inventories/:id/accept', to: 'inventories#accept', as: 'lender_accept'
post 'inventories/:id/decline', to: 'inventories#decline', as: 'lender_decline'
Mailer view:
<%="#{button_to 'YES', lender_accept_url(borrow), method: :post, id: "accept #{borrow.id}", style: "background-color:green; color: white; width: 40px; display: inline"} %>
<%="#{button_to 'NO', lender_decline_url(borrow), method: :post, id: "decline #{borrow.id}", style: "background-color:gray; width: 40px; display: inline"}" %>
I did an inspect element on the email as well just to confirm that the button_to was generating the appropriate code:
<form action="inventories/2037/decline" method="post" target="_blank" onsubmit="return window.confirm("You are submitting information to an external page.\nAre you sure?");"><div><input style="background-color:gray;width:40px;display:inline" type="submit" value="NO"></div></form>
<div>
<input style="background-color:gray;width:40px;display:inline" type="submit" value="NO">
</div>
</form>
In my mailer settings, I set the host properly to my domain name, so when I get the email and click the button, I get taken to /inventories/2037/decline appropriately, but I still get the error, because apparently the logs say I'm still trying to go for GET... why is that??
2014-08-17T06:18:03.206205+00:00 app[web.1]: ActionController::RoutingError (No route matches [GET] "/inventories/2037/decline"):

Wondering if there's a better way of doing this?
Making GET request is ideal solution. Don't use any other request. It either be not supported by email client and potentially add big security hole.
It is your misconception that big websites use post/patch requests to perform such action in email. They all use get requests, they include some token which is tied up/trigger that particular action when user visits there website.
OK tried the button_to to generate a form to POST to avoid using GET to do POST as was the "patchy" solution above, still not working.
Obviously, it won't work. There is no rails JavaScript file. Moreover, even if you include it it won't work either, because of csrf-token token. External form submission is not allowed by default in rails application.
My suggestion is to send user to your dashboard with some extra parameters like www.example.com/dashboard?take_action=accept and use JavaScript to trigger the required action. This will is best practice. This will avoid you using lender_accept request as get.
To to more precise, don't do this.
get 'inventories/:id/accept_email', to: 'inventories#accept', as: 'lender_accept_email'
get 'inventories/:id/decline_email', to: 'inventories#decline', as: 'lender_decline_email'
This is bad. Since, get request isn't for updating/deleting.
Since, you want to link this actions into your email, you should put dashboard link with some extra parameter. Now, with JavaScript perform accept/decline action whenever you see those parameters which you passed to your email.
More:
Can I put both Get and Post URL link in an email
Is it possible to perform a HTTP POST from a HTML email?
Is it possible to send a POST request from within an Outlook email?

I'm pretty sure the link in the email will not be able to use "PATCH" method because the email doesn't have rails.js. (https://github.com/rails/jquery-ujs/blob/master/src/rails.js)
I don't think embedding rails.js will work though because most email legit providers will probably disable javascript in emails.
You can add get 'inventories/:id/accept', to: 'inventories#accept', as: 'lender_accept' in your route to circumvent this problem, but if using get is bad practise if you are actually changing anything in database.
My easy solution would be to link the user to some page where they can accept or decline on your website rather than accept or decline from the email.

Related

rails get entire current url but change the http protocol

i need to create a link in my rails view that basically takes the user from the normal http page to the same page but using https.
i currently have something like
CLICK ME!
however it:
does not include the parameter string
just looks hack-ish to me
is there a better way to take the user from http to the same relative request path with all of the parameters?
Try something like
<%= link_to "CLICK ME!", named_url(protocol: 'https') %>
Where named_url is the route to whatever you want to link to.
EDIT
Since you don't have a named route and just want to use whatever page the user is on, you can do something like:
<%= link_to "CLICK ME!", "https://#{request.host}#{request.fullpath}" %>
You can find more about the request object here: http://guides.rubyonrails.org/action_controller_overview.html#the-request-object

Haml: link_to vs button_to

From what I understand, link_to is used for get methods, and button_to is used for post methods.
On the other hand, I was told that with HTML5 semantics, <button> is used for any type of clickable...well, button. In the case I have a clickable button that sends a user to a form to fill out, should I create a button_to or a link_to?
It's simpler that you think.
That methods are Rails helpers and don't have anything to do with haml.
Yes, one method is for get and another for post methods. If you need to post any data to controller, use button_to (for example when deleting a record). Otherwise, link_to is enough.
Moreover, you can make link_to posting data using :method parameter:
= link_to "Something", some_path, :method => :post
Answering your question, use link_to.
The main principle difference between the #link_to, and #button_to is that the #link_to just creates a link tag A, and makes simple AJAX request without an additional data, while #button_to creates a FORM with a custom data, so the form can be used to make extended AJAX request to a webserver. The form data includes embedded CSRF-token, which is used to authentication the request. In case of #link_to CSRF-token must be serualized and send in on_click event.
You should use links to point the user to a resource, like an article.
But you have to tend to use buttons to point to an action(like "Create"/"Send" on your edit page). If this doesn't agree with your interface -- style them like as a link.
Here's why: you cannot point your user to any non-GET action via link_to if he lacks the javascript support. So, buttons are the only options to make your send/destroy action to be triggered in this case.
Feel free to use both approaches if your link points to a page that eventually leads to a modification of a resource (link/button to an edit/create page that shows a form), like in your case.
If you want to simply send a user to somewhere, it is get request. So you should use link_to in this case. By the way, you can use the link_to for post requests and other requests (like button_to too) if you will specify :method. For example: =link_to "some path", some_path, :method => :get

the method problem and the difference between url and path

when i read the book "Aglie web development with rails 4th",i found the code
<%= button_to 'Add to Cart', line_items_path(:product_id => product) %>
what's the difference if i use "line_items_url" and the code doesn't has the method like :method=>:post,
why?
The path version produces relative urls such as /order/34/lines/ while the url version produces a full url such as http://localhost:3000/order/34/lines/.
The second form is often used in mailers when the user click a link in a mail client or in an external webmail.
In your site you won't notice any difference.
Moreover the :method=>:post option will produce a post request to your webserver. It will do that by adding a javascript code which will create a form on the fly, add parameters to it and do a submit call to send your browser to the requested page with a post method.
The _url helper generates an URL that includes the protocol and host
name. The _path helper generates only the path portion.

Symfony/Routing: Using POST to Avoid Query Params in URL

I want to pass the id from one action to the next action, but I do not want it seen in the URL. Is there a way to hide it?
Using Symfony, I have created a sign-up done page whose URL should be /signup/done
When the form is processed, I want to redirect to the signupSuccess action and pass the recently created ID for future use. So I use...
$this->redirect('#signup_done?id=' . $sign_up->getId());
The routing is as follows:
signup_done:
url: /signup/done
param: { module: SignUp, action: signupDone }
I have avoided the :id at the end because I don't want it in the URL.
But the resulting URL is /signup/done?id=1
Just as an experiment, I tried putting this on a template.
<?php echo link_to('Sign-up again', '#signup_done?id=1', 'post=true') ?>
Even when using post, the query parameter appears in the URL.
The need is: I want to pass the id from one action to the next action, but I do not want it seen in the URL. Is there a way to hide it?
I set the id as a parameter in the request using $request->setParameter('id', $id) and it was available in the next action.
This kept the URL clean.
If you want to post, you need a form. Back in symfony 1.2 there were helpers that you could call and made you just that - but they were removed for the promotion of promoting good code.
Depending on how you want the 'Sign up again' text to look, you can either create a simple form and a submit button, or create a link, attach a click listener, and create a form there via JS, finally post it.
Any parameter that you pass to the route in url_for, link_to and such end up in the get parameters.

Multi-step form using GET requests

We have the unusual requirement of a multi-step form through GET requests. So, instead of POSTing the forms, we're using GET requests to pass new parameters to the query. So, the basic idea is that there's a treatment, and a date passed to the query. A three-step form if you will.
Show available treatments, pick one
Show available dates (there's business logic in the background that figures these out)
Pick a time
The URL will go through the following states
site.com/bookings/new
site.com/bookings/new/[id|name_of_treatment] (by this, I mean it could either by the ID field or the name of the the treatment)
site.com/bookings/new/[id|name_of_treatment]/2010-12-12/
So, my route looks like this:
map.connect 'bookings/new/:massage_type/:date', :controller => :bookings, :action => :new
massage_type is synonymous with the treatment_id.
So my form_tag looks like this:
<% form_tag( {:action => "new"}, :method => "get" ) do %>
The problem I'm having is that I want it to just give me back the URL site.com/bookings/new/[id|name_of_treatment]/ but instead it gives back the following URL:
http://localhost:3000/bookings/new?massage_type[treatment_id]=1&commit=actionnew
I don't suppose anyone knows?
Forms that use GET are adding the input values as query parameters. There's no way to make the form post to a different URL, where the input values are part of the URL instead - it's just not supported by the HTML standard.
You could use URL rewrite to remap the incoming URLs of this type to the one you want, however that's not really a good solution, because this would result in a second request.
However, what I don't understand is why does the form need to do GET to that specific URL. Is it a requirement that these URLs can be constructed by the user manually, instead of using the form?
If there is no such requirement, I would advise to use standard POST form to http://localhost:3000/bookings/new and modify the form in the response based on the parameters in the POST body, as necessary.
Better yet, write some Ajax that would update the form according to the user's choice, without making a full form submit, until the user has finished all the choices.
By definition, the result of a GET request will have a query string (?param1=value1&param2=value2&...) in its URL. To get rid of those, you'll have to either start using POST or immediately redirect to the desired URL upon receiving a GET request.
I rather like the redirect approach because it doesn't show that confusing/annoying message about resubmitting POST data when the user refreshes their browser.

Resources