How can I exclude a page from browser's history? - spring

I want to exclude page-1 from the browser history, so that when I navigate away from it, (let's say to page-2) and then press the back button, it won't show page-1.
Or else, if going from 1 -> 2 disables the back button on 2.
Is there any way this can be accomplished? (Not using javascript)
I've a form on page-1, on successful submission of which page 2 is shown.
However, I don't want page-1 to be shown when I click on back button on page-2.
Either it should be disabled or it should revert form submission (deleting all the session variables) and then show page 1.
How can this be done?
I'm using Spring MVC and JSP views in my project.

You could write a session parameter when displaying the page to enforce the user to visit page1 before they visit page2.
#RequestMapping("/page1.html")
public String page1(HttpSession session){
session.setAttribute("seen_page_1", "true");
return "page1";
}
#RequestMapping("/page2.html")
public String page2(HttpSession session){
if(!"true".equals(session.getAttribute("seen_page_1").toString())){
return "redirect:/page1.html";
}
return "page2";
}
If you want to display page2 to users who have already visited page1 (even when they return to the page), you could use a cookie value instead.

Depending on how long you want this behavior to last you can store the form status from page 1 in a session var or cookie. Session vars will last as long as the user has their browser open. You can set the expiration for a cookie to last a few minutes or to never expire.
You can set up a filter between requests for page 1 and page 2. In the filter check for either the session var or cookie. If the value is for a submitted form then redirect to page 2. If the value is not set or set to something else (depending on your implementation) then redirect to page 1.

Related

old data is shown when navigating back after an AJAX request

I have the following scenario while programming a shop using asp.net core 1.0 (mvc):
I add 3 products to my shop basket.
on the shop basket I delete one of the 3 products using an AJAX request. after the product has been deleted, I move on to the next step and become redirected to the page with the delivery & payment settings
on the page with the delivery & payment settings I choose to navigate back to the shop basket. I expect to see 2 products, but I see 3 since the page hasn’t been reloaded from the server.
when I inspect fiddler, I notice that no requests to the various resources of the shop basket are performed.
in my BaseController class I’ve already set the following code which should disable the cache:
[NonAction]
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
context.HttpContext.Response.Headers.Add("Cache-Control", "no-store, must-revalidate");
context.HttpContext.Response.Headers.Add("Expires", "0");
context.HttpContext.Response.Headers.Add("Pragma", "no-cache");
await next();
}
does anybody have an idea about how we could navigate back and see the up-to-date shopping basket containing only 2 products?
you can use this simply piece of code:
<head data-page-key="#string.Concat(this.ViewContext.RouteData.Values["controller"], #"_", this.ViewContext.RouteData.Values["action"])"
data-page-token="#DateTime.Now.Ticks.ToString()">
<script type="text/javascript">
var pageKey = document.getElementsByTagName('head')[0].getAttribute("data-page-key");
var pageToken = document.getElementsByTagName('head')[0].getAttribute("data-page-token");
var pageTokenCached = sessionStorage[pageKey];
if (!pageTokenCached || pageTokenCached !== pageToken) sessionStorage.setItem(pageKey, pageToken);
else location.reload();
</script>
The browser's sessionStorage stores the data for only one session. The data is deleted when the user closes the specific browser tab.
Here every time the page comes from server, a new data-page-token value is generated, stored in the tag and saved in browser's sessionStorage.
When the user click on browser BACK botton, the html page is retrieved from the browser cache, and executed. So, the cached token is retrieved from sessionStorage, compared with what we have in the page, that is data-page-token.
If the values are equal it means that the page comes from browser cache otherwise it is a page coming from server.
When data-page-token is equals to sessionStorage's token, it means that we have a stale page and so it is reload, otherwise is processed.
session Storage is compatible with all browser and also supported by IE 8.0

Wicket (1.6) stateless form resets paging

I've already tried to find anything about that problem but I guess I either was not sure how to shortly describe the problem to find a solution or nobody else had that before which I can't think of. Maybe my thinking is wrong, too.
I have a stateless wicket 1.6 form with an ajax supporting panel (WebMarkupContainer with output id). The panel holds a dataview with paging navigator. The dataview itself is filled by a DataProvider.
The panel shows some entries from the database and below that is the navigator. by clicking any page on the navigator, the panel is refreshed (ajax) and shows content from that page. The page itself is not re-rendered by the browser.
When I now leave the page by navigating to another internal page (so basically when leaving the dataview-panel-page in any way) to open a detail page or so and then return to that dataview-page the navigator is resetted (because it's stateless I guess). The navigator can't remember which page to show and begins at the top of the first page again.
The question is: How can I solve this? I would like to i.ex. navigate to page 2 and then temporary leave the page for another internal page. When returning I want to be on page 2, focussed on the record where I clicked the link to "details" before. This also happens when I just open a new page in a new Browser tab.
Thank you!
Here's some code:
final WebMarkupContainer gamesPanel = new AjaxContainer("gamesPanel");
final DataView<Game> dataView =
new GameDataView("gameOverview", targetCurrencyModel, searchTextModel, gameFilterModel,
new GameDataProvider(searchTextModel, gameFilterModel, targetCurrencyModel));
dataView.setItemsPerPage(ITEMS_PER_PAGE);
gamesPanel.add(dataView);
final XNPagingNavigator navigator = new XNPagingNavigator("navigator", dataView);
navigator.setOutputMarkupId(true);
add(navigator);
You guys can try what I mean: The page I'm talking about is http://www.xbox-now.de. Just navigate to page 2, then click on details and return to main page.
I think you might use History API to push a new state when you click on navigation bar. In the new URL for the state you can include a parameter that indicates the index of the current navigator page.
You can customize your AJAX link in order to perform this operations when user click on it.
For more details on History API see Updating address bar with new URL without hash or reloading the page
I solved this in the NoWicket framework by introducing a model aware page cache which reuses page instances when hashcode/equals matches in the page model. You can see the implementation in the GitHub repo, try this implementation of the IPageFactory wrapper as a starting point, but it is more code involved there, just check out the code, debug the example and navigate the code to understand it better in order to apply it to your application. See your use case in action by trying this data table example in the documentation website (which you can also debug locally): http://invesdwin.de/nowicket/ajaxdatatable
Try changing the paging index, navigate to a different page and navigate back to the data table example page. You will still see the paging index that you left there.
Thank you guys for your replies. I've done it now another way. Used the base code from here and modified it in some ways. Added some nice css AttributeModifiers to indicate the actual page:
item.add(new AttributeModifier("class", new PageLinkCssModel(pageable, pageIndex, "active")));
Then I modified some code to add or reset the page parameter, that it's 1) used only once and 2) keeps all the actual page parameters which were there before adding own ones. So I am just appending the page number now. This way I can keep my initially mount path like www.foo.bar/path/path/path. Complete URL would now look like: www.foo.bar/path/path?page=123.
To pass my entered page (i.e. page=5) to the data provider I just had to override the providers iterator. It should start with the page I entered. Due to the ugly generated navigator URLs (which are extremly bad for SEO) I now have nice looking urls which are working independently what wasn't possible before. And that's also the reason why I could not get back to the correct page. The navigator URL was not lookup-able by wicket.
new DataView<GamePrice>("gamePriceOverview", new GameDetailDataProvider(gameId, targetRegion) {
#Override
public Iterator<GamePrice> iterator(final long first, final long count) {
return super.iterator(ITEMS_PER_PAGE * getCurrentPage(), count);
}
getCurrentPage() comes from the base template and gets the actual page number entered (if one is entered):
public long getCurrentPage() {
// -1 weil zero based
return getRequest().getQueryParameters().getParameterValue("page").toString() != null
? (getRequest().getQueryParameters().getParameterValue("page").toLong() - 1)
: 0;
}
So instead of having ugly SEO-unfriendly URLs which are also not compatible to work independant (directly enter the generated url) I now have the same URL I expect with an added page-parameter.
URL now would looks like:
http://localhost:8080/game/4249/de/doom-preorder?page=2
URL before was:
localhost:8080/game/4249/DE/doom-preorder?0-1.ILinkListener-gamePrices-navigator-navigation-2-pageLink
If I now go back from the detail page to the main index with active "Bookmarkable-Navigator", I correctly come back to the page and position where I left it (because of bookmarkable page links).
That's how I achieved this problem with a nice bonus: User- and SEO-friendly URLs.

Struts2 Ajax issue

The problem is the following. When user clicks on the link I load the data into a div, but when the session of the user is expired I need to redirect him to login page, or if the is external error I need to redirect the user to error page. The problem is, if for example my interceptor sees that the user is no longer in session and tries to redirect him to login page, the login page is again loaded in the div. the other elements on page remain. How can I make the page reload without JavaScript? I mean directly from struts.xml.
You need to send to your browser whether it is a new session or not.
One way.
In your interceptor add a header to indicate whether it is a new session. Then if you are using jquery you can bind the ajax complete and check the header for the attribute that its a new session. And finally on new session, do a location.reload.
your ajax for jquery would look something like this
jQuery(document).ajaxComplete(function(e, xhr, settings) {
var status = xhr.status;
if(status == 200 ){
var isAuth = xhr.getResponseHeader('_isnewsession');
if((isAuth == '1')){
alert('Your Session has timed out!');
location.reload();
}
}
});
Does that make sense?

Back Button Clicked and Error Messages on Previous Page Still Display

I have a very simple 1 page form using ASP.NET MVC 3.
It has 3 inputs. If the user enters the correct inputs and the database retrieves a pdf, that pdf is displayed in the next page. I have used the PRG pattern, in so far as it is possible in this scenario:
public ActionResult Index()
{
SomeModel model = new SomeModel
{
... properties
};
return View(model);
}
[HttpPost]
public ActionResult Index(SomeModel model)
{
byte[] blob = someService.GoGetDetails(model);
TempData.Add("blobkey", blob);
return RedirectToAction("DisplayPdf");
}
public ActionResult DisplayPdf()
{
return File(TempData["blobkey"] as byte[], "application/pdf");
}
The problem use case is as follows.
User enters incorrect details and clicks Submit button. Red error text displays.
User then enters correct details and clicks Submit button. Pdf displays.
User THEN hits back button. Form displays with previous red error text visible.
A tester has made up a requirement that the red text should not be there when the back button is clicked.
Seems easy. Until you try. Does anyone have the magic bullet for this.
One thing I tried as a compromise did not work. That is, I tried to kill caching on the browser with this code inside the DisplayPdf acttion method:
this.ControllerContext.HttpContext.Response.Expires = 60;
this.ControllerContext.HttpContext.Response.ExpiresAbsolute = DateTime.Now.AddSeconds(-1);
this.ControllerContext.HttpContext.Response.AddHeader("pragma", "no-cache");
this.ControllerContext.HttpContext.Response.CacheControl = "no-cache";
It has no effect. Perhaps that works in the webforms world. Not here with the FileResult.
When back button is hit, there is no request to the server. It is all client-side. So, I do have access to javascript. But javascript has no access to the http headers, so there's no way of differentiating from where the page was loaded.
I'm keen to see if this has been solved before. My Googling came up empty.
Thanks
OK. I'm now satisfied. If :
Amazon.com
JsFiddle.net
Outlook.com
Dropbox.com
all exhibit the same behavour, then my work is not falling short of best practice.
Intercepting the navigation buttons of browsers seems very hacky to me. This is one use case where we have to make sure our code does no damage when users do dumb stuff. But resolve elegantly? Probably not a big driver when control is taken out of your hands by the browser.

Loading a page from url does not work, but page work when I click Refresh

I have a URL that displays a customer list like this:
http://domain.com/pls/apex/f?p=724:2:820875406836801:::::
The list of customers are displayed with the title being linked to Page3 & request has CustomerId
When I click the URL http://domain.com/pls/apex/f?p=724:3:21712451478201::NO:RP,3:P3_CUSTOMER_ID:82, Page 3 is loaded correctly with details of selected customer. But the "Update" and "Delete" action buttons never work.
But, if I click the browser refresh button and then try to perform an update or delete, it works.
I don't know where I could be going wrong. Can someone give me hints?
I am not using BRANCH_TO_PAGE_ACCEPT in my URL link definition.
It looks like you have the session ID hardcoded in the URL on page 2:
http://domain.com/pls/apex/f?p=724:2:820875406836801:::::
The session ID is 820875406836801, whereas:
http://domain.com/pls/apex/f?p=724:3:21712451478201::NO:RP,3:P3_CUSTOMER_ID:82
The session ID has mysteriously been changed to 21712451478201. I'm not sure, but I suspect that you've hardcoded the session ID in your report on page 2. This has the effect of causing a new login session to be created when page 3 is opened (and maybe this is why the update/delete buttons don't work - but you haven't told us what the error message is so I'm not sure); refreshing the page may be restoring the session.
If I'm right, what you need to solve this issue is to use the session variable (&SESSION.) in your report on page 2 instead of hardcoding it, e.g.:
http://domain.com/pls/apex/f?p=724:3:&SESSION.::NO:RP,3:P3_CUSTOMER_ID:82
The issue was with the way the url was created. First of all, I should not set only 1 thing (Title) to be a url. It should be the entire div. Like below.
<li><div style="">
<a href="f?p=&APP_ID.:2:&SESSION.::NO::P2_PK_PROJECT_ID:#LINK#" rel="external">
<h3>#TITLE#</h3>
<p><strong>#BOLD_TEXT#</strong></p>
<p>#PLAIN_TEXT#</p>
</a></div>
</li>
A report row template with the above code was created. This template is used in my Customers List page. Now each customer is a link (Title, Name, etc). The link href is also hard-coded. Note that I am passing ProjectID:#LINK# #LINK# refers to a value like 1, 2 etc
Now clicking this, loads page 2 correctly and Apply Changes & Delete button are now clickable.

Resources