I have a wicket application. The application is basically a single page application and everything is done using ajax calls. I have a requirement to open a piece of information in another tab or window. There are several ways to do so, using html
target = "_blank"
or using code via PopupSettings. All these work in way that they open the new page/tab with the data I need.
The problem is that when I click on an ajax action on the origin page, after opening the other window/ tab, I get an error saying the component belongs to a different page. After debugging I understand that once I call the new page, my old page is no longer stored in the page store manager cache. What happens is that on request commit (which is called at the end of the request cycle) the following is called on org.apache.wicket.page.PageStoreManager:
public synchronized void setSessionCache(final List<IManageablePage> pages) {
sessionCache = new ArrayList<>(pages);
afterReadObject = null;
}
with the "pages" list argument holding a single page, the new page, thus replacing the current page in the cache. Once I call the the ajax call, instead of receiving the page from cache I receive a deSerialized version of the page from the store which is a different instance then the one connected to the component used for the click behaviour, and I get the error.
Am I doing something wrong? Is this a bug? an architectural flow? Appreciate any light shed on this.
I'm using wicket 8.9.0
Here is the call to the page to render on a new tab. currently it is on a non ajax button inside the page:
protected void onClick() {
UserSession.get().setAttribute(Constants.documentRef, obj);
this.setResponsePage(DocumentViewPage.class);
}
The button tag has target="_blank".
Here is the error I get:
ava.lang.IllegalArgumentException: Component [RealmPanel [Component id = realm]] cannot be updated because it is on another page.
at org.apache.wicket.page.PartialPageUpdate.add(PartialPageUpdate.java:451)
at org.apache.wicket.ajax.AjaxRequestHandler.add(AjaxRequestHandler.java:241)
at org.apache.wicket.ajax.AjaxRequestHandler.add(AjaxRequestHandler.java:234)
at com.centimia.smartAdmin.frame.controller.Navigator.updateRealm(Navigator.java:474)
at com.centimia.smartAdmin.frame.controller.Navigator.setCurrentPanel(Navigator.java:249)
at com.emi.client.main.panel.folder.FolderMenuProvider$3.onClick(FolderMenuProvider.java:179)
at com.centimia.smartAdmin.frame.component.menu.MenuItem$1.onClick(MenuItem.java:76)
at org.apache.wicket.ajax.markup.html.AjaxLink$1.onEvent(AjaxLink.java:85)
at org.apache.wicket.ajax.AjaxEventBehavior.respond(AjaxEventBehavior.java:127)
at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.onRequest(AbstractDefaultAjaxBehavior.java:598)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.internalInvoke(ListenerRequestHandler.java:306)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.invoke(ListenerRequestHandler.java:280)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.invokeListener(ListenerRequestHandler.java:222)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.respond(ListenerRequestHandler.java:208)
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:914)
at org.apache.wicket.request.RequestHandlerExecutor.execute(RequestHandlerExecutor.java:65)
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:282)
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:253)
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:221)
at org.apache.wicket.protocol.ws.AbstractUpgradeFilter.processRequestCycle(AbstractUpgradeFilter.java:70)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:206)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:299)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1602)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1588)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1345)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1557)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1247)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:502)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:364)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
Related
I'm using Laminas framework (laminas/mvc v3.1) / Zend3. In certain cases I need to call a different action than prescribed for a given route, i.e. display a default page to users who are not authorised to view restricted content.
To do this, I've tweaked the onDispatch method in Module.php as follows:
public function onDispatch(MvcEvent $event) {
// check if content is restricted to the current user
if (content-is-restricted) {
$event->getRouteMatch()->setParam("controller", "{namespace}\Controller\{anotherController}");
$event->getRouteMatch()->setParam("action", "{defaultAction}");
$event->getRouteMatch()->setMatchedRouteName("{defaultRoute}");
}
else {
proceed-as-normal-to-the-action-prescribed-by-route
}
}
Unfortunately, the snippet above renders the following error:
A 404 error occurred
Page not found.
The requested controller was unable to dispatch the request.
Controller:
{namespace}\Controller\{anotherController}
No Exception available
This works though if the action is within the same controller but not if it's in a different one. What am I missing?
Note, I don't want to redirect the user and change the URL - just substitute what is showed to them
Hi I have a resource controller in Laravel 4. It has all the default methods generated by artisan's controller:make.
Models etc are in place.
User clicks on a link in a view that does a URL::route to a named route pointing at a controller action. It points to the 'store()' method in the controller, which is meant to be a POST method.
I write my code in the 'store()' method to handle this request. It uses eloquent to insert data into db. It returns a plain text response with HTTP code 200.
When user clicks on the above mentioned link (that points to the store() method), it seems the browser simply jumps to the index (GET) of that controller and the code doesn't run because the store() method is bypassed.
When I move all code from within the store() method into the index() method, everything works as expected.
What am I doing wrong here that my 'store()' method is not handling my code. Even when creating URL to the store action directly using URL::action, this fails.
Can someone please enlighten me?
Code:
Store method:
public function store()
{
$itemsArray = Session::get('sdata');
$cartItem = new Cart;
$cartItem->session_id = Session::get('sid');
$cartItem->items = json_encode($itemsArray);
$cartItem->save();
return Response::make('an item was added to carts', 200);
}
View:
Go
Same result for this view also:
`Go`
This is because <a> tag, is able to send only GET request. Try to create a new method, for example addToCart, and then set new rout on routes.php
In my application I mount following URL:
this.mountPage("/details/${site}", MerchantDetailPage.class);
So a request to for instance ../details/anything will create an instance of MerchantDetailPage with pageparameter: site=anything.
The constructor of MerchantDetailPage:
public MerchantDetail(final PageParameters parameters) {
super();
org.apache.wicket.util.string.StringValue storeParameter = parameters.get("site");
if (!storeParameter.isEmpty()) {
this.store = this.service.getStoreByQBonSiteWithCategoriesDescriptionsRegionAndAddress(storeParameter.toString());
}
if (store == null) {
throw new RestartResponseAtInterceptPageException(Application.get().getHomePage());
}
// Build the page
this.createPage(this.store, null);
}
This seemed to work fine until I noticed that the constructor was called 4 times.
After some digging I found that the constructor was called once with parameter site=anything but then another 3 times for 3 images that are on the page; e.g.:
<img wicket:id="store_no_image" src="./images/shop_no_logo_big.png" alt="logo" />
So, for this resource Wicket is also calling this page but with parameter: site=images.
As a consequence, the store is null so the request for the image is redirected to the homepage => the image is not found.
Why is this happening? Why is wicket trying to treat a resource request through a page mount?
Some side comments:
MerchantDetailPage has also another constructor which is called directly from the code and accepts the store id as a parameter. In this case the problem does not occur.
if I use an absolute URL for the image it does work (does not enter into MerchantDetailPage for the image request)
Well... your page resides at
/detail/anything
which is correctly mapped to your merchant detail page...
Your images reside at
/detail/images/shop_no_logo_big.png
and similar, which is correctly mapped to your merchant detail page...
The mount path doesn't know and doesn't care if it's a page request or a resource request. For all it is worth it could be the case that you're using the mount path to create the resource dynamically...
So the solution is to move your images to a location that doesn't match yout mount-path.
One of the application I am developing using Zend Framework requires the user's profile page to be accessed via website.com/username, while other pages should be accessed by website.com/controller_name/action_name
I am not too sure how can this be achieved, however, I feel this can be done with some tweaks in the .htaccess file.
Can someone here please help me out?
Many thanks in advance
As suggested before, you can use a custom route that will route single level requests. However, this will also override the default route. If you're using modules, this will no longer work example.com/<module>.
I have done this before but only for static pages. I wanted this:
example.com/about
instead of this:
example.com/<some-id>/about
while maintaining the default route so this still works
example.com/<module>
example.com/<controller>
The way I did this was using a plugin to test if my request could be dispatched. If the request could not be dispatched using the default route, then I would change the request to the proper module to load my page. Here is a sample plugin:
class My_Controller_Plugin_UsernameRoute extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
if (!$dispatcher->isDispatchable($request)) {
$username = $request->getControllerName();
$request->setModuleName('users');
$request->setControllerName('dashboard');
$request->setActionName('index');
$request->setParam('username', $username);
/** Prevents infinite loop if you make a mistake in the new request **/
if ($dispatcher->isDispatchable($request)) {
$request->setDispatched(false);
}
}
}
}
What about using Zend_Controller_Router_Route, look here the link http://framework.zend.com/manual/en/zend.controller.router.html#zend.controller.router.routes.standard.variable-requirements
I'm using TempDate["Message"] to show little update banners as the user does things on my site like this:
[AcceptVerbs(HttpVerbs.Post), Authorize(Roles = "Admins")]
public ActionResult Delete(int id)
{
_Repo.DeletePage(id); // soft-delete
TempData["Message"] = "Page deleted!";
return RedirectToAction("Revisions", "Page", new { id = id });
}
Then in my master page I have this:
<%-- message box (show it only if it contains a message) --%>
<% string Message = (TempData["Message"] ?? ViewData["Message"]) as string;
if(!string.IsNullOrEmpty(Message)){
%>
<div id="message"><%:Message %></div>
<% }
TempData["Message"] = null; ViewData["Message"] = null; %>
I hit both TempData and ViewData because I read somewhere that TempData should be used for redirects and ViewData should be used otherwise.
The issue is: often the message won't show up right away. Sometimes it takes a click or two to different parts of the site for the message to show up. It's very strange.
Any ideas?
You should verify all places where you use TempData["Message"] in your code. Corresponds to ASP.NET MVC does browser refresh make TempData useless? you can read TempData["Message"] only once (see also http://forums.asp.net/p/1528070/3694325.aspx). During the first uage of TempData["Message"], the TempData["Message"] will be deleted from the internal TempDataDictionary.
Probably it would be better to use TempData["Message"] only inside of Revisions action of the Page controller and not inside of master page or inside a View.
TempData is not intended to pass data to views, hence the name ViewData for that purpose. In fact, I can't think of a reason to use TempData from within a view definition at all...
One very common usage of TempData is the passing of information between controller actions when you do a redirect (the Revisions action in your example above, for instance, would be able to make use of your TempData["Message"] variable).
This is common practice in the PRG means of coding MVC interactions (Post-Redirect-Get) since you often need to pass information from the initial target action when doing the Redirect to the Get. An example of how this might be useful in a Get is below where I often just default to a new viewmodel UNLESS there is one already passed from a redirect in TempData:
public ActionResult System() {
SystemAdminVM model = (SystemAdminVM)TempData["screenData"] ?? new SystemAdminVM();
One more thing; I see you explicitly clearing your TempData and ViewData dictionary entries in your view. You don't need to do that as by that point they are at the end of their life spans anyway...
Happy coding!
Your app's behavior is the one you'd expect if you're using TempData where you should be using ViewData.
You want to double-check that you're storing your status feedbacks in TempData only when the controller does a re-direct. Otherwise, you should use ViewData.
This smells like you need a couple of unit tests to confirm the behavior you're seeing. Try writing up a couple using this example as a starting point:
http://weblogs.asp.net/leftslipper/archive/2008/04/13/mvc-unit-testing-controller-actions-that-use-tempdata.aspx
If you have configured multiple worker process for your application, but session state mode is "InProc", then you can't use default TempData implementation, as session state becomes unusable. (see ASP.NET session state and multiple worker processes)
You could try to use MvcFutures CookieTempDataProvider instead.