Combination View Flat View Tree View
Threads [ Previous | Next ]
toggle
Linh Viet Nguyen
p:filedownload, p:dataExporter with validation
November 9, 2017 8:02 PM
Answer

Linh Viet Nguyen

Rank: New Member

Posts: 19

Join Date: February 25, 2008

Recent Posts

Hi,
I'm newbie on liferayfaces.
How to use primefaces p:filedownload, p:dataExporter with form validation?
I tried to test on primfaces application portlet (added <p:inputText name="test-validate" required="true"></p:inputText> in form 2 ):
 1                    <h:form id="f2" enctype="multipart/form-data" prependId="false">
 2                        <p:inputText name="test-validate" required="true"></p:inputText>
 3                        <p:confirmDialog
 4                            message="#{i18n['are-you-sure-you-want-to-delete-this']}"
 5                            severity="alert" widgetVar="confirmation">
 6                            <p:commandButton id="confirm"
 7                                actionListener="#{applicantBackingBean.deleteUploadedFile}"
 8                                oncomplete="PF('confirmation').hide()"
 9                                update="uploadedFilesTable" value="#{i18n['yes']}" />
10                            <p:commandButton id="decline" onclick="PF('confirmation').hide()"
11                                value="#{i18n['no']}" type="button" />
12                        </p:confirmDialog>
13                        <h3>#{i18n['attachments']}</h3>
14                        <p:commandButton id="exportButton" ajax="false"
15                            icon="ui-icon-disk" value="#{i18n['export-as-csv']}" immediate="false">
16                            <p:dataExporter fileName="uploaded-files"
17                                target="uploadedFilesTable" type="csv" />
18                        </p:commandButton>

when click download with form validarion, the page reload in resource phase so the UI is broken :
http://localhost:8081/web/guest/testpage?p_p_id=1_WAR_comliferayfacesdemoprimefacesapplicantportlet310_INSTANCE_tQoCfiqurUqp&p_p_lifecycle=2
Thanks,
Kyle Joseph Stiemann
RE: p:filedownload, p:dataExporter with validation
November 10, 2017 11:19 AM
Answer

Kyle Joseph Stiemann

LIFERAY STAFF

Rank: Liferay Master

Posts: 578

Join Date: January 14, 2013

Recent Posts

Hi Linh,
In a portlet evironement, the implementation of p:dataExporter requires the use of the RESOURCE_PHASE to export the data. Due to this restriction, other inputs including commandButtons and inputTexts (which require the ACTION_PHASE to function correctly) should not be included in the h:form alongside p:dataExporter or p:fileDownload (with the exception of ajaxified command components since Ajax requests are also executed in the RESOURCE_PHASE).

Could you provide more detail on what you are trying to accomplish (with some simple sample code)? Perhaps we can provide an alternative solution.

- Kyle
Linh Viet Nguyen
RE: p:filedownload, p:dataExporter with validation
November 10, 2017 5:56 PM
Answer

Linh Viet Nguyen

Rank: New Member

Posts: 19

Join Date: February 25, 2008

Recent Posts

Thank Kyle,
I'm migrating our 2 mvc portlets to liferayfaces (Liferay CE 6.2).
1st portlet is:
1- Guest input data in application form.
2- When click save, system save data to database (with form validation) and export to pdf file. If not success, error message show on screen.
2nd portlet:
2- Download a report (in old search container ) with captcha validation
Are there alternative solution?
Thanks.
Kyle Joseph Stiemann
RE: p:filedownload, p:dataExporter with validation
November 13, 2017 9:46 AM
Answer

Kyle Joseph Stiemann

LIFERAY STAFF

Rank: Liferay Master

Posts: 578

Join Date: January 14, 2013

Recent Posts

Okay, I think I've figured out a solution. Since the other form inputs must be submitted and validated via Ajax, you can include them in the form and submit them with an Ajax commandButton. Once the Ajax request is complete, you can execute some JavaScript to click on a hidden Export button to cause the data to be exported. That way the user only needs to click once to submit info and export data (although those actions will actually occur across two requests).

example.xhtml:

<h:form enctype="multipart/form-data">

<h:panelGroup id="inputs">
<p:inputText id="input" value="#{bean.inputValue}" required="true" />
<p:message for="input" />
</h:panelGroup>

<p:commandButton value="Submit + Export"
actionListener="#{bean.clickExportButtonAfterAjaxRequest}" update="inputs" process="inputs @this" />

<p:commandButton id="hiddenNonAjaxExportButton" value="hiddenNonAjaxExportButton" ajax="false"
style="display: none;">
<p:dataExporter fileName="data" target="dataTable" type="csv" />
</p:commandButton>

<p:dataTable id="dataTable" value="#{bean.data}" var="dataRow">
<!-- ... your dataTable code here ... -->
</p:dataTable>

<!-- ... your code here ... -->
</h:form>


Bean.java:

@ManagedBean(name = "bean")
@RequestScoped
public class Bean {

public void clickExportButtonAfterAjaxRequest(ActionEvent actionEvent) {

RequestContext requestContext = RequestContext.getCurrentInstance();
UIComponent component = actionEvent.getComponent();
UIComponent hiddenNonAjaxExportButton = component.findComponent("hiddenNonAjaxExportButton");
String hiddenNonAjaxExportButtonClientId = hiddenNonAjaxExportButton.getClientId();
requestContext.execute("document.getElementById('" + hiddenNonAjaxExportButtonClientId + "').click();");
}

// ... your code here ...
}


On the user's side all that they see is that they've clicked a button to submit the form and export the data (seemingly in 1 action or request), but in reality, the form was submitted and validated in an Ajax request and the data was exported in a non-Ajax request. This ensures that the data is never exported until the form inputs are valid. Then the data is exported because the Export button is clicked via JavaScript. Note that validation and model value updates will occur twice for the inputs---once for the Ajax request and once for the non-Ajax export request. You might be able to skip the first or second model value update (since the same value will be submitted in the non-Ajax export request) using FacesContext.renderResponse(), but I couldn't find an easy way to do that. Other than that duplicate update, I think this is the simplest possible way to validate/submit data and export data on the same button press with p:dataExporter. Note that validation must be performed on both the Ajax and non-Ajax export requests since a hacker could potentially click the hidden export button without clicking the Ajax one (thus submitting invalid data or bypassing validation completely if validation is disabled).

- Kyle
Kyle Joseph Stiemann
RE: p:filedownload, p:dataExporter with validation
November 13, 2017 12:20 PM
Answer

Kyle Joseph Stiemann

LIFERAY STAFF

Rank: Liferay Master

Posts: 578

Join Date: January 14, 2013

Recent Posts

I didn't notice that you also wanted to perform captcha validation before exporting the file in your second portlet. If you simply replace the p:inputText with portal:captcha, the code will not work, since the second request will fail to validate the captcha. In order to work around this, I'd recommend un-rendering the captcha after it is validated the first time by adding the following code to the clickExportButtonAfterAjaxRequest() actionListener:

component.findComponent("captchaId").setRendered(false);

In theory, a user could fill out the captcha once and request the data export multiple times since this workaround only prompts for the captcha once, but I doubt that would be a problem. If you are extremely concerned about that being a potential problem, you could send an Ajax request after the data is exported to re-render to captcha.

- Kyle
Linh Viet Nguyen
RE: p:filedownload, p:dataExporter with validation
November 13, 2017 8:35 PM
Answer

Linh Viet Nguyen

Rank: New Member

Posts: 19

Join Date: February 25, 2008

Recent Posts

Thank Kyle.
Tomorow, I've read carefully liferayfaces demo and found another solution using CustomResourceHandler (in jsf-export-pdf-portlet).
I executed resourceURL in form submit action like that:
1ApplicationExportResource resource=new ApplicationExportResource(application);
2String url = resource.getRequestPath();
3RequestContext.getCurrentInstance().execute(
4        "window.location.href = '" + url + "'");

But the problem is the resourceURL is easy to guess with resource primary key so i must add a secure key in getRequestPath method. Is it OK?

 1if (requestPath == null) {
 2            StringBuilder buf = new StringBuilder();
 3            buf.append(ResourceHandler.RESOURCE_IDENTIFIER);
 4            buf.append("/");
 5            buf.append(getResourceName());
 6            buf.append("?ln=");
 7            buf.append(getLibraryName());
 8            buf.append("&");
 9            buf.append(PARAM_NAME_CUSTOMER_ID);
10            buf.append("=");
11            buf.append(getCustomer().getCustomerId());
12            buf.append(PARAM_NAME_SECURE_KEY);
13            buf.append("=");
14            buf.append(getCustomer().getSecureKey());
15            requestPath = buf.toString();
16            requestPath = facesContext.getExternalContext().encodeResourceURL(requestPath);
17        }

I also tested your solution and it work well.
Now I have 2 options to solve my problem.
Linh,
Kyle Joseph Stiemann
RE: p:filedownload, p:dataExporter with validation
November 14, 2017 6:55 AM
Answer

Kyle Joseph Stiemann

LIFERAY STAFF

Rank: Liferay Master

Posts: 578

Join Date: January 14, 2013

Recent Posts

Hi Linh,
Thanks for posting an alternative solution. If you are trying to make the URL unguessable for security reasons, you may want to take a look at this StackOverflow Q&A.

- Kyle

Participate in the State of Liferay Community 2017. Help the community and even win some prizes!