Why is Turbo not honoring link_to method: :post? - turbo

For a long time, Rails has provided a method: :post option in the link_to helper: when the option was given, Rails would intercept the click and issue a POST request, instead of the default GET request.
However, for some unknown reason, this is not working in Rails 7: despite adding method: :post to my link_to helper, Rails sends a GET request (not a POST request). I thought Turbo was supposed to take care of it, but it does not seem to be happening.
This is what you can do to reproduce, very simple steps:
$ rails new example_app
$ bin/rails g scaffold Book title
$ bin/rails db:create && bin/rails db:migrate
$ echo "<%= link_to "New book", new_book_path, method: :post %>" >> app/views/books/index.html.erb
$ bin/rails s
Now visit localhost:3000/books from your web browser, and click on the second "New book" link. I would expect getting an error (after all, I did not configure the proper POST route) but, unfortunately, Rails isues a GET request - and not a POST request, as it should have:
Started GET "/books/new" for ::1 at 2021-12-27 17:40:43 +0100
Processing by BooksController#new as HTML
Rendering layout layouts/application.html.erb
Rendering books/new.html.erb within layouts/application
Rendered books/_form.html.erb (Duration: 9.1ms | Allocations: 5216)
Rendered books/new.html.erb within layouts/application (Duration: 10.2ms | Allocations: 5594)
Rendered layout layouts/application.html.erb (Duration: 12.9ms | Allocations: 7759)
Completed 200 OK in 25ms (Views: 13.6ms | ActiveRecord: 4.3ms | Allocations: 12404)
Why is this happening? Shouldn't Turbo intercept the link, and, as Rails UJS did in the past, send a POST request?

It seems to me that the Rails 7 docs have not been updated for Turbo and the missing UJS library. Even though the link_to documentation clearly states that link_to(..., ..., method: :post) should work, it clearly does not.
Diving into Turbo's documentation, there is a section called Performing Visits With a Different Method where it says to use link_to ..., ..., data: { 'turbo-method' => :post }, which DOES work.

I faced a similar issue, but I fixed it differently than proposed here:
Pinned the #rails/ujs
$ bin/importmap pin #rails/ujs
and then in application.js added:
import Rails from "#rails/ujs"
Rails.start()
And link_to "...", "...", method: :delete started working for me.
Consider this solution when you have a legacy app with a bunch of method: links and you don't want to change them to turbo-method:

Rodrigo Serrano‘s answer works for POST. However, if turbo-method is set :delete, and redirect_to is used in the destroy action, after the 302 redirection, it will trigger the destroy action on the resource which is redirected to.
button_to does not have this problem.
I am using rails 7.0.1. There is an open issue https://github.com/hotwired/turbo-rails/issues/259 .

Faced the same issue in ruby 3.04, rails 7. The post method was not honoured and was intercepted as an get request. Using a quick fix for now by changing link_to to an button.
<%= button_to "Logout", :logout, method: :post %>

Related

Rails 6 shrine image_tag is broken but open in new tab is not

Started up a brand new Rails 6 app with Shrine, everything is fresh out of the box following along with the getting started guide. Got the file uploaded successfully but on the show page the img tag is just a broken image icon but if I open it in a new tab, it shows. No errors showing in the console.
/config/initializers/shrine.rb
require "shrine"
require "shrine/storage/file_system"
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads") # permanent
}
Shrine.plugin :activerecord # loads Active Record integration
Shrine.plugin :cached_attachment_data # enables retaining cached file across form redisplays
Shrine.plugin :restore_cached_data # extracts metadata for assigned cached files
app/uploaders/image_uploader.rb
class ImageUploader < Shrine
# plugins and uploading logic
end
app/models/content.rb
class Content < ApplicationRecord
include ImageUploader::Attachment(:image) # adds an `image` virtual attribute
end
views/contents/show.html.erb
<p>
<strong>Image:</strong>
<%= image_tag #content.image_url %>
</p>
page source
<img src="/uploads/efe42a8e0d19d3ffcabd26bcddd71473.mp4">
Figured it out. I pulled the "gif" I was testing with from Imgur, which is actually an mp4 and thus not going to show in an "image_tag". Changed it to "video_tag" and it's working.

View property returns 'undefined' when following a linkTo route

I have a custom view to display breadcrumbs for the current model navigation on a page, here's how it looks in my template (which is the singular model template):
<div class="breadcrumbs">
{{view Mdm.BreadcrumbView}}
</div>
Here's what my view looks like to render out the breadcrumbs:
App.BreadcrumbView = Ember.View.extend
templateName: 'breadcrumb'
penultimate: (->
content = #get('controller.content.self_and_ancestors')
content[content.length - 2]
).property('controller.content')
last: (->
content = #get('controller.content.self_and_ancestors')
content[content.length - 1]
).property('controller.content')
Here's the breadcrumb template that renders the data:
<ul class="breadcrumb">
{{#if view.penultimate}}
<li>{{#linkTo group view.penultimate}}Back{{/linkTo}}</li>
{{/if}}
<li>{{view.last.name}}</li>
</ul>
My model that the view is using (controller.content) has nested data self_and_ancestors that hold the breadcrumb data that I need for the current page I'm on. I'm only interested in seeing the last object in that array and the one just before it to navigate one page back (that's what the penultimate and last are displaying). The breadcrumbs render perfectly and I can navigate fine until I click the 'Back' (penultimate) link. When I hit that I got the following error:
Uncaught TypeError: Cannot read property 'length' of undefined
For some reason the content is undefined, but I'm not exactly sure why. Any thoughts?
Edit:
Welp, here's an example http://jsbin.com/ejazin/2. It is, however, working in this example, which makes me thing something else may be the problem. Basically every time you click a breadcrumb link in my app, the router does a find based on the ID which makes an ajax call to find the current group for that route and all sub groups. Maybe the view is trying to pull out the sub_groups data before it's come back from the server... Is there any way to check / prevent this?
Edit Edit:
This might convey what I'm trying to do a bit more - http://jsbin.com/obazus/1/.
Edit Edit Edit:
Ok, the issue is definitely the fact that the view is rendering and calling penultimate before the ajax request has finished in the model. That being said - what's the best way to make sure it renders after the ajax request for the model is finished?
After looking more closely at your use case, what might help is to check out the new router facelift here. It's still not wildly documented and should soon appear on the ember website as a blog post, but it's already merged into ember-latest so you will have access to the new implementation. Especially the beforeModel & afterModel hook's seam to be relevant for you.
For example in the afterModel hook you can do stuff like:
App.PostsIndexRoute = Ember.Route.extend({
afterModel: function(posts, transition) {
if (posts.length === 1) {
this.transitionTo('post.show', posts[0]);
}
}
});
Hope it helps.

asp.net webform default routing does not work

EDIT: I solved by myself.Cause the stylesheet reference path is /css/style.css not css/style.css,url will not be fixed by asp.net.I found that <link> <meta> and <title> will add as an server control to head when head tagged with runat="server",So these server control will auto fix current reference problem.
!!!BUT,<script> is ignored,One of the solutions is <%= ResolveClientUrl('~/js/jquery.js') %>.But It does not work when you have a theme attched to master page,asp.net canot add stylesheets in App_Themes to head when it contains such <% %> expression,asp.net will throw an exception.
So it seems like the best solution is using <ScriptManager>.
Another important discover is that when you have a <ContentPlaceHolder> in master runat=server head,stylesheet inside <Content> of child page using this master page will not be treat as a server control.So in <Content> of child page you must use <%= ResolveClientUrl%> to handle url fix.
ORINGIAL:
I'm running asp.net 4.0 on IIS 7 Express. I route "MarketList/{type}" to "~/MarketList.aspx" with default value new {type = 0}. The URL "localhost:4888/MarketList" just works well, I can recieve default value "0".
But I found that the "/" will mess up the stylesheet and javascript references defined in "Main.master" master page. "Main.master" is in the root level with "MarketList.aspx". The stylesheet in "Main.master" is defined as css/style.css. The "css" folder is also at the root level. When I'm accessing by "/MarketList", it works well. But, "/MarketList/1" gives the value "1" to {type}. The URL of the stylesheet in the page becomes "../css/style.css" which points to "/MarketList/css/style.css". This doesn't exists (obviously).
So, I decide to use "-" to split those parts, I route "market-list-{type}" with same setting just like above. But, I found I can not access the default routing URL which I thought would be "localhost:4888/market-list-". "localhost:4888/market-list" does not work either. Only "/market-list-0" will work.
Could someone help me?

browser view zcml for="IPloneSiteRoot" returns 404

I wonder if anyone else has seen this before:
I have the following ZCML:
<browser:page
for="*"
name="workflow_action"
class=".bika_listing.WorkflowAction"
permission="zope.Public"
/>
When I try to visit siteroot/workflow_action, Plone tells me "This page does not seem to exist…"
The ZCML is being read - if I cause an error in the ZCML, Plone fails to start.
I've tried for="Products.CMFPlone.interfaces.IPloneSiteRoot" with the same result.
The code in WorkflowAction is not being invoked at all - if I place a pdb in __call__, it does not fire.
I have two similar ZCML definitions for different contexts, which work fine and look something like this:
<browser:page
for="bika.lims.interfaces.IAnalysisRequest"
name="workflow_action"
class=".analysisrequest.WorkflowAction"
permission="zope.Public"
/>
If I remove these other views, leaving only one view in the system with name="workflow_action", (the one for IPloneSiteRoot or "*"), the result is the same.
This seems like a very simple view - I don't know what other information I can put here?
Using Plone 4.0.7.
it must be me!?
Most likely your browserview raises an exception in the __init__() method and so the exception is ignored and never displayed, and the result usually is a 404 error.
I have a similar issue when add a Solgema.NavigationPortlet portlet on a Private state folder, then I found the browser:page navTreeitem in navigation.py raised a KeyError, which cause a 404 response for client browser.
Thanks for Giacomo's answer.

If a user doesn't have permission to render a View (configured on configure.zcml), how do I raise Forbidden instead of redirecting to login_form?

I have a browser view, with some utilities. Is mainly an "utility view" that I traverse using old-style pt templates (that are inside skins folder). My browser/configure.zcml:
<browser:page
for="*"
name="my_view"
class=".myview.MyView"
allowed_interface=".myview.IMyView"
permission="my.permission"
/>
As you can see, it has a custom permission: this is needed because anonymous users can't render this view and this permission is really specific to a certain situation in my portal.
I thought: I'm going to try to render the view in my template.pt: since I've already set a permission in browser/configure.zcml, when trying to render Plone itself is going to handle this for me. So I did in my template
<span tal:define="my_view here/##myview">
</span>
So far, so good. A user without my.permission trying to get into /Plone/template.pt will fail. But Plone redirects to the login form, and I would prefer to raise a Forbidden exception instead. Something like:
<span tal:define="my_view here/##myview | here/raiseForbidden">
</span>
...but, of course, this doesn't work since the view rendering didn't throw an error. (I know here/raiseForbidden doesn't exist, it's here/raiseUnauthorized that is usually used but the concept is the same)
My question is: is it possible to do it? Configuring my permission somewhere, or configuring some method in my view (like render or __call__), that when a user doesn't have permission to render it, an exception like Forbidden is raised?
Plone redirects to the login form because you raise Unauthorized. If you want different behaviour you'll need to do something different.
In this case, you could directly redirect the user to a new page with an error message tailored to the situation.
actually you don't need to do this:
"tal:define="my_view here/##myview>"
because for browser views there's a default variable named "view" that already contain your class.
For raising an exception you should remove permission check from the zml directive and modify your class as below:
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
class YourBrowserView(BrowserView):
""" .. """
index = ViewPageTemplateFile("templates/yourtemplate.pt")
...
def __call__(self):
if not getSecurityManager().checkPermission(your.permission, self.context):
raise Unauthorized("You are not authorized! Go away!")
else: return index()
Bye
Giacomo

Resources