My login view with #Route("login") implements HasUrlParameter<String> with the following lines:
#Override
public void setParameter(BeforeEvent event, #WildcardParameter String parameter) {
event.rerouteTo(AnotherView.class);
}
The content of the AnotherView is shown when I call the login view but the URL remains from the login view, i.e. it is /login instead of /another-view. Is that works-as-designed? I would expect a history entry to be added in the browser with the new URL. How can I achieve that? Also, I tried ui.navigateTo instead of rerouting but that didn't do anything.
The issue is known on github project of Vaadin flow: https://github.com/vaadin/flow/issues/4189.
Related
When I try to navigate a user asynchronously from the server-side via #Push like:
ListenableFuture<JobApplicationMatrix> listenableFuture = // some Async method invocation
var ui = UI.getCurrent();
listenableFuture.addCallback(jobApplicationMatrix -> {
ui.access(() -> {
ui.navigate(CandidateComplianceApplicationView.class, new RouteParameters(CandidateComplianceApplicationView.APPLICATION_UUID_PARAMETER, candidateApplicationUuid));
});
}
it is not working as expected. CandidateComplianceApplicationView is a secured View. When I execute ui.navigate from ui.access the system moves me to the Keycloak login page. Looks like the system doesn't understand that the user is logged in. So, is this possible to navigate to the secure view from ui.access and if so, what am I doing wrong here?
I also tried to change #PermitAll to #AnonymousAllowed on CandidateComplianceApplicationView. In such case the system moves me to the correct page but the actual content of the CandidateComplianceApplicationView is not rendered at all.
UPDATED
I use
#Push(transport = Transport.LONG_POLLING)
So I followed the Kontent doc from the github which allows to retrieve content from a link (https://github.com/Kentico/kontent-delivery-sdk-net/wiki/Resolving-links-to-content-items)
First I implement a resolver to redirect when we click on the link like this :
public class CustomContentLinkUrlResolver : IContentLinkUrlResolver
{
public string ResolveBrokenLinkUrl()
{
return "/404";
}
public string ResolveLinkUrl(ContentLink link)
{
switch(link.ContentTypeCodename)
{
case "author":
return $"/author/{link.UrlSlug}";
default:
return $"/not_found";
}
}
}
Then I register my resolver within a IDeliveryClient
client = DeliveryClientBuilder
.WithProjectId(myid)
.WithContentLinkUrlResolver(new CustomContentLinkUrlResolver())
.Build();
At this moment if i click on the link it will redirect to /author/linkName with an error on the page what I think is normal
I don't get the last part of the doc (how just by doing a getString on the contentItem the link will work ?) so I would like to know how to display the content on the redirect page
I don't know if i was clear enough and sorry for my english
Here is the error thrown on the redirect page
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
The last part of the wiki article refers to something that you already have:
At this moment if i click on the link
If you have a link that you can click on, then you have done what that part of the article describes.
What you need is to resolve the request. If you are getting a 404 that you expect, then you know that you need to add a route to your application to handle the request. In the handler (a controller, a component, etc.) extract the urlSlug from the route and use it with a IDeliveryClient to retrieve the item and then render the content. You will need to filter the GetItems call with something like new EqualsFilter("elements.urlSlug", urlSlug).
My Vaadin 7 application doesn't react on browser url changing. For example I entering from keyboard a new url parameters and pressing Enter key and after that nothing is changing.
Application only reacts on F5 or page refresh button.
How to also make Vaadin 7 application to respond to Enter key after url update ?
UPDATED
I'm using Vaadin com.vaadin.navigator.Navigator object.
For example I have an url: http://example.com/#!products/30970
When I change the url in browser address bar (for example to http://example.com/#!products/34894) and press enter key I would like to change information at my page in order to show info about product with id 34894 instead of product with a previous id 30970.
Vaadin Navigator and UriFragmentChangedListener
Right now I'm using Vaadin Navigator in order to define views:
Navigator navigator = new Navigator(this, viewDisplay);
navigator.addView("products", ProductView.class);
First time in web browser I'm successfully able to access this view with product id parameter for example by the following url:
http://example.com/#!products/30970
ProductView is constructed first time and in its public void enter(ViewChangeListener.ViewChangeEvent event) method I'm able to get uri parameters.
But after that when I change product id in web browser address bar in this url to another one(for example to 30971 in order to display information for another product):
http://example.com/#!products/30971
and press Enter key the view is not refreshed and doesn't react on these changes..
As suggested in the comments I have added UriFragmentChangedListener listener and now at least able to handle URL fragment changes(after Enter key presing).
Now, my logic have to react on these changes and I'm looking for a correct way how it should be implemented in Vaadin 7.
So, If I understood correctly - in additional to Navigator logic I also have to use this listener and inside of this listener logic I have to get a reference on appropriate view(navigator.getCurrentView() ?) object and invoke some method on this object in order to change internal view state without full view rebuild ? If I'm correct - is there some standard mechanism in Vaadin in order to simplify this job ?
i can not think of another way than pass the UriFragmentChangeEvent manually to your View. I guess the Vaadin API can not do it automatic.
public class MyUI extends UI{
#Override
protected void init(final VaadinRequest request) {
/*
* UriFragmentChangedListener
* when URL+Parameter manuell eingegeben werden
*/
getPage().addUriFragmentChangedListener(new UriFragmentChangedListener() {
#Override
public void uriFragmentChanged(UriFragmentChangedEvent event) {
View currentView = getNavigator().getCurrentView();
if(currentView != null){
if(currentView instanceof UriFragmentChangedListener){
((UriFragmentChangedListener)currentView).uriFragmentChanged(event); //custom method
}
}
}
});
}
}
To make this work add UriFragmentChangedListener to your ProductView:
public class ProductView extends CustomComponent implements View, UriFragmentChangedListener {
}
take a look at the Parameters in the ViewChangeEvent of your ProductView
By using this url structure "http://example.com/#!products/30970", you can read the product id as following:
#Override
public void enter(ViewChangeEvent event) {
String productId = event.getParameters();
}
I have one taskflow and have two views like this image:
I have a button to go there, but I don't need to click the button. I need to redirect programmatically to another view with calling the action.
How to call it, because I am using JSFF as a page?
If I use this method:
FacesContext fctx = FacesContext.getCurrentInstance();
UIViewRoot root = fctx.getViewRoot();
//client Id of button includes naming container like id of region.
RichCommandButton button =
(RichCommandButton) root.findComponent("cb1");
ActionEvent actionEvent = new ActionEvent(button);
actionEvent.queue();
}
I get an error like can't find the component. If I use page template id I also get an error like the compiler can't find the component.
Do you have another method to solve this, or can you fix it if I get wrong find component?
You have use "URL view" and redirect it to your path :
For such case i use this code:
public static void navigateTo(String redirect) {
FacesContext facesContext = FacesContext.getCurrentInstance();
NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
nh.handleNavigation(facesContext, null, redirect);
}
where redirect param is a name of flow case rule.
I am using Primefaces DialogFramework with
Primefaces 5.0
Mojarra 2.1.27
Glassfish 3.1.2.2 Build 5
My problem is, that if the user knows the location of my dialog, he is able to access it directly via the URL. I do not want that to be possible, so I thought it would be able to put the dialog in WEB-INF folder of my web-app, but now, if I want to open the dialog, I get a FileNotFound-Exception.
If my dialog is located in some regular folder, it works fine
RequestContext.getCurrentInstance().openDialog("/myfolder/mydialog");
// this works as expected
but if it is located in WEB-INF, it does not work any longer
RequestContext.getCurrentInstance().openDialog("/WEB-INF/mydialog",options,null);
// this is causing a fileNotFoundException
I also tried to set up a navigation rule for this in faces-config but again with no success
<navigation-case>
<from-outcome>mydialog</from-outcome>
<to-view-id>/WEB-INF/mydialog.xhtml</to-view-id>
<redirect />
</navigation-case>
How may I open dialogs located in WEB-INF folder, or is it not possible at all?
Thanks in advance
Unfortunately, putting PrimeFaces Dialog Framework dialogs in /WEB-INF in order to prevent direct access is indeed not going to work. The dialogs are loaded entirely client side. On the POST request which opens the dialog, JSF/PrimeFaces returns an oncomplete script with the (public!) URL of the dialog to JavaScript/jQuery, which in turn shows a basic dialog template with an <iframe> whose URL is set to the dialog URL, which in turn loads the content. In effects, 2 requests are being sent, the first to get the dialog's URL and the second to get the dialog's content based on that URL in the <iframe>.
There's no way to keep the dialog in /WEB-INF without falling back to the "traditional" dialog approach via <p:dialog> and conditional display via JS/CSS. There's also no way in the server side to verify based on some headers if the request is coming from an <iframe>, so that all others could simply be blocked. Your closest bet is the referer header, but this can be spoofed.
One way to minimize abuse is checking the presence of pfdlgcid request parameter (identified by Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM) when a dialog is being requested. PrimeFaces namely appends this request parameter representing "conversation ID" to the dialog URL. Presuming that all dialogs are stored in a folder /dialogs, then you could do the job with a simple servlet filter. Here's a kickoff example which sends a HTTP 400 error when /dialogs/* is being requested without the pfdlgcid request parameter.
#WebFilter("/dialogs/*")
public class DialogFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String id = request.getParameter(Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM);
if (id != null) {
chain.doFilter(req, res); // Okay, just continue request.
}
else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); // 400 error.
}
}
// ...
}
However, the abuser might not be that stupid and discover the pfdlgcid request parameter during the normal flow and still be able to open the dialog individually when supplying that parameter, even with a random value. I thought of comparing the actual pfdlgcid value to the known ones. I checked the PrimeFaces DialogNavigationHandler source code, but unfortunately, PrimeFaces doesn't store this value anywhere in the session. You'd need to provide a custom DialogNavigationHandler implementation wherein you store the pfdlgcid value in the session map which in turn is also compared in the servlet filter.
First add the following method to the DialogFilter:
public static Set<String> getIds(HttpServletRequest request) {
HttpSession session = request.getSession();
Set<String> ids = (Set<String>) session.getAttribute(getClass().getName());
if (ids == null) {
ids = new HashSet<>();
session.setAttribute(getClass().getName(), ids);
}
return ids;
}
Then copypaste the PrimeFaces DialogNavigationHandler source code into your own package and add the following line after line 62:
DialogFilter.getIds((HttpServletRequest) context.getExternalContext().getRequest()).add(pfdlgcid);
Replace the <navigation-handler> in faces-config.xml with the customized one.
Finally, alter the if condition in the DialogFilter#doFilter() method as follows:
if (getIds(request).contains(id)) {
// ...
}
Now, this prevents the abuser from attempting to open the dialog with a random ID. This however doesn't prevent the abuser from attempting to open the dialog by copypasting the exact <iframe> URL immediately after opening it. Given the way how the PrimeFaces dialog framework works, there's no way to prevent that. You could at most remove the pfdlgcid value from the session when the dialog is about to returns to the parent. However, when the dialog is closed by pure JS means, then this is also bypassed.
All in all, if you really, really, want to avoid the enduser being able to open the dialog individually, then you can't go around the "traditional" <p:dialog> approach.