I am working on a TYPO3-backend-module. The main "overview"-Action allows to upload a file via ajax upload, which gets processed in one of my controllers.
The corresponding controller-action instantiates a StandaloneView, renders it and writes the rendered content to the response body.
My problem is, that I have no controllerContext set in the generated content, therefore a form, which should trigger a different extbase action, in that rendered content does not work, because the modulename is not set.
Can anybody tell me how I could possibly get/pass the controllerContext, to make it available in the controller action, which is called via ajax ?
Best regards and thanks in advance, Oliver !
The main problem here is that BE modules don't exactly use a standard View implementation - in the default implementation, they wrap around a StandaloneView which by definition has no controller context.
That being said, if you can change the way the View wraps around the StandaloneView, the controller context can be copied by doing:
$view->getRenderingContext()->setControllerContext($parentView->getRenderingContext()->getControllerContext());
Alternatively you may want to copy the entire rendering context rather than just the controller context.
Advise applies to TYPO3v8 only. On 7.6 you will need to copy the Request instead.
For usages outside of MVC context you will need to manually pass the original controller context values such as controller name and module name. That means the values must be part of the link you create and must be read by the receiving code (in whichever way that code's context requires) and then manually assigned to a Request you create and pass to the sub-view. Note that this is only necessary if those MVC context values actually change - if you always call the same action next, you can get the right result by simply hardcoding the desired extension name, module name, controller name and action in the form/link in the template you render from that AJAX controller.
Related
From a form "A" I want to open a specific form "B" when I click on a Many2One field.
I placed into the context of this field the usual
'form_view_ref': 'model.form_id'
but this works only when "A" is in edit mode. If it is in view mode it opens instead the "C" form. I noticed that JSON request just "lose" the context i set and send the standard empty one (with just timezone/uid etc.)
I never realized this before. Is it some kind of bug?
#Alessandro Ruffolo,
context is variable between server client, and context has common attributes like uid, active_d, active_ids, active_model, User timezone, user lang. When calling an ORM method, you will probably already have a context - for example the framework will provide you with one as a parameter of almost every method.
If you do have a context, it is very important that you always pass it through to every single method you call. But when you don't pass context somewhere on any method in python or js client side server will generate new context, and their are so many place with old in core code that context is broken by not passing.
When you don't pass context, it breaks context and prepare new context.
With New v8 API context is more consistent as that’s not required.
Bests
I suspect I'm doing this wrong.
For various reasons, my app forces the user to make some choices right after login. In order to ensure that they enter the necessary data, I override the OnActionExecuting method in a base controller class to intercept any attempt at executing an action before this data has been entered, and redirect the user to the necessary page. I preserve the url of the action they were attempting to execute with the following code:
url = Url.RouteUrl("Default", filterContext.RouteData.Values);
(filterContext is an ActionExecutingContext object, and a parameter of OnActionExecuting.)
The problem I'm having is that, if the action was associated with a controller in an area, the url I get doesn't reflect the area.
I understand from other posts that I can get the area name from the DataTokens collection of RouteData. But I'm uncertain of the best way to pass it. I suppose I could retrieve it and use the RouteValueDictionary.Add method to add it to RouteData.Values (assuming Values is not read-only at that point; I don't know), but that feels a bit ... odd, like somehow the point is being missed.
Is this really the way this is supposed to be done? Is there something wrong elsewhere, that Area is absent from my RouteData.Values?
I would just take it out of the data tokens in the filter and add it to route values. You can do it with RouteValues.Add:
if (filterContext.RouteData.DataTokens.ContainsKey("area"))
filterContext.RouteData.Values.Add("area",
filterContext.RouteData.DataTokens["area"]);
The areas feature was added in MVC2, and I imagine this is a side effect of it not being in MVC1. However, as long as your RouteValues contains an "area" key, UrlHelper.RouteUrl should generate the correct URL for the area.
I have a tiny application in MVC 3.
In this tiny application, I want my URLs very clear and consistent.
There's just one controller with one action with one parameter.
If no value is provided (that is, / is requested by the browser), then a form is displayed to collect that single value. If a value is provided, a page is rendered.
The only route is this one:
routes.MapRoute(
"Default",
"{account}",
new { controller = "Main", action = "Index", account = UrlParameter.Optional }
);
This all works fine, but the account parameter never appears in the address line as a part of the URL. I can manually type test.com/some_account and it will work, but other than that, the account goes as a post parameter and therefore does not appear. And if I use FormMethods.Get in my form, I get ?account=whatever appended to the URL, which is also not what I want and which goes against my understanding. My understanding was that the MVC framework would try to use parameters set in the route, and only if not found, it would append them after the ?.
I've tried various flavours of setting the routes -- one route with a default parameter, or one route with a required parameter, or two routes (one with a required parameter and one without parameters); I've tried mixing HttpGet/HttpPost in all possible ways; I've tried using single action method with optional parameter string account = null and using two action methods (one with parameter, one without), but I simply can't get the thing appear in the URL.
I have also consulted the Steven Sanderson's book on MVC 3, but on the screenshots there are no parameters either (a details page for Kayak is displayed, but the URL in the address bar is htpp://localhost:XXXX/).
The only thing that definitely works and does what I want is
return RedirectToAction("Index", new { account = "whatever" });
But in order to do it, I have to first check the raw incoming URL and do not redirect if it already contains an account in it, otherwise it is an infinite loop. This seems way too strange and unnecessary.
What is the correct way to make account always appear as a part of the URL?
My understanding was that the MVC framework would try to use
parameters set in the route, and only if not found, it would append
them after the ?
Your understanding is not correct. ASP.NET MVC doesn't append anything. It's the client browser sending the form submission as defined in the HTML specification:
The method attribute of the FORM element specifies the HTTP method used
to send the form to the processing agent. This attribute may take two
values:
get: With the HTTP "get" method, the form data set is appended to the URI specified by the action attribute (with a question-mark ("?")
as separator) and this new URI is sent to the processing agent.
post: With the HTTP "post" method, the form data set is included in the body of the form and sent to the processing agent.
ASP.NET MVC routes are used to parse an incoming client HTTP request and redispatch it to the corresponding controller actions. They are also used by HTML helpers such as Html.ActionLink or Html.BeginForm to generate correct routes. It's just that for your specific scenario where you need to submit a user entered value as part of the url path (not query string) the HTML specification has nothing to offer you.
So, if you want to fight against the HTML specification you will have to use other tools: javascript. So you could use GET method and subscribe to the submit handler of the form and inside it manipulate the url so the value that was appended after the ? satisfy your requirements.
Don't think of this as ASP.NET MVC and routes and stuff. Think of it as a simple HTML page (which is what the browser sees of course) and start tackling the problem from that side. How would you in a simple HTML page achieve this?
I know that we can design the layout in *.xml then in the action just invoke loadLayout, and renderLayout to render the blocks/views.
But, I have a question is:
- How can I load the layout at runtime?
If we have an action which does not really design its layout and will be decided how to render at runtime.
You can please consider the answer from the question for more clear.
Writing a new answer because it seems that you actually DO still want to render, you just want to render a different route's layout XML updates. I believe the _forward() method from Mage_Core_Controller_Varien_Action will allow you to do what you are describing with the least amount of pain.
You should add your action controller directory ahead of the catalog directory, create a ProductController with a viewAction, and check customer is not logged in - in this check you would call $this->_forward('customer','account','login');.
This approach though is going to require more effort in order to be usable, as I imagine that you want the user to be sent to the product page upon login. Have you seen Vinai Kopp's Login Only Catalog module? It should do this for you.
loadLayout() and renderLayout() just execute block output method toHtml() (usually) and take the resulting strings and apply them to the response object via appendBody(). In an action controller you can just call $this->getResponse()->setBody('response string'). How you build the string is up to you.
You can also use Mage_Core_Block_Flush to immediately send output to the browser without using the response object.
Is it possible to prevent direct access to an action in symfony. The action is only accessible by "forward" only. So basically, a way to see if the request is coming from another action.
I'm trying to achieve this because the first action handles plenty of verifications then if it fails, it stays on that action. If it succeed, it will forward to an appropriate action; this action needs to have safe inputs (validated from the first action). In order to keep the code DRY, the second action doesn't need to re-verify all the inputs again.
Then why not doing simply a private method? The second action is sort of a plugin, it's decided on the fly where it's going from the first one, that action has its own set of other future action/template. It makes more sense to simply forward instead of trying to handle plenty of cases that Symfony already takes care of.
There are multiple ways to achieve this.
1) Make sure your action isn't accessible by the routing. If you have wildcard routes this will be harder, but you can always add a route which would point the url for your action to a 404 page. Add something like this to your routing.yml:
disabled_action:
url: /disabledController/disabledAction
params: { module: default, action: error404 }
2) Check the action stack upon executing your action. The action stack let's you know from which action you were redirected. You can access it within your action using $this->getController()->getActionStack(). If the getSize() is bigger than 1 (in a default configuration) you we're forwarded.
Use referrer parameter available in request
$request->getReferer() will give you full url of previous action
I'm curious why you're trying to achieve this. Are you looking to have multiple points of access that forward to this action? What if you simply defined a private method (which by default aren't web-accessible) and called it directly from another action?