Primefaces upload, how to only allow one upload in advance mode - jsf

I am wondering if it possible, by using the primefaces upload in advance mode to limit the user uploading one file only, currently i have :
<p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
multiple="false"
update="messages"
sizeLimit="100000000"
allowTypes="/(\.|\/)(gif|jpe?g|png|doc|docx|txt|pdf)$/"
auto="false"/>
<p:growl id="messages" showDetail="true"/>
as you can see i have muliple ="false" but a user is still able to upload multiple files, any tips ?
EDIT :
<p:fileUpload widgetVar="upload" fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
multiple="false"
update="messages"
label="Select File"
sizeLimit="100000000"
allowTypes="/(\.|\/)(gif|jpe?g|png|doc|docx|txt|pdf|html)$/"
auto="false"/>
<p:growl id="messages" showDetail="true"/>
have added the widgetVar above
and in my js
<script type="text/javascript">
function Naviagtion()
{
//alert("Sent to the printing holding queue, you may close this app now, your work will still print out ");
window.setTimeout(afterDelay, 500);
location.href = 'FilesUploaded.xhtml';
}
upload.buttonBar.find('input[type=file]').change(function() {
if (this.value) {
var files = upload.uploadContent.find('.files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
</script>
but i am still able to multi upload, am i going about this in the right direction

Although better behavior to solve it should be as #BalusC suggested, but in primefaces 4.0 I am seeing the attribute
fileLimit="1"
which you can set to 1 to disallow multiple file additions using "Choose" button. When user adds more file then it simply says
"Maximum number of files exceeded"

The multiple="false" only tells the webbrowser to disable multiple file selection in the browser-specific Browse dialog. However, it indeed doesn't prevent the enduser from clicking multiple times on the Choose button of the PrimeFaces file upload section to browse and add a single file multiple times.
Your best bet is to bring in some JS/jQuery to remove all previously selected files when a new file is selected. Provided that you have given your <p:fileUpload> a widgetVar="upload", then this should do:
$(document).ready(function() {
upload.buttonBar.find('input[type=file]').change(function() {
if (this.value) {
var files = upload.uploadContent.find('.files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
});
Works for me on PrimeFaces 3.5.

If you have file limit set to 1 and some error happens while file is loading - you have to refresh the page to get file upload work again. If you don't refresh the page you get out of limit error message.
I used solution with JS, like in accepted answer,but have to change selectors, because wigetWar did not work for me.
In my view i have :
<p:fileUpload id="objectUpload"... />
In my portlet theme, table with files has a css class of "ui-fileupload-files".
$(document).ready(function() {
$("div[id*='objectUpload']").find('input[type=file]').change(function() {
if (this.value) {
var files = $("div[id*='objectUpload']").find('.uifileupload-files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
});
Hope it helps.I used PrimeFaces 6.0

Related

<p:growl/> wont show message when using PrimeFaces.MonitorDownload

I am using PrimeFaces.Monitordownload function to monitor the progress of file download. First the file is created then downloaded. If anything goes wrong, a error file is created and downloaded with streamed content instead of real file. This is fine and works well. The problem occurs when I want to add a error message along with the error file. I have no idea how to update my growl.
When using PrimeFaces.monitorDownload, I need to set ajax=false. Could this be part of my problem?
I have tried multiple things, like setting update="growl" on the p:commandButton, autoUpdate=true on the growl itself, but nothing. I might be doing something wrong in my backing bean, but don't think so.
Here is my relevant Java code:
boolean fileCreated = processor.start();
if(fileCreated){
File xlsfile = new File(filePath);
InputStream stream = new FileInputStream(xlsfile);
file = new DefaultStreamedContent(stream, "file/xlsx", fileName);
return file;
}else{
fileName = fileName.replace(".xlsx", ".txt");
filePath = rootPath + fileName;
File xlsfile = new File(filePath);
InputStream stream = new FileInputStream(xlsfile);
file = new DefaultStreamedContent(stream, "file/txt", fileName);
message = new FacesMessage(FacesMessage.SEVERITY_WARN, "Something went wrong!", "Please send an email to servicedesk and alert the administrators of this page!");
FacesContext.addMessage(null, message);
return file;
}
And my xhtml:
<p:dialog modal="true" widgetVar="statusDialog" header="Status"
footer="Creating file. It might take up to 30 minutes, depending on the file size."
draggable="true" closable="false" resizable="false">
<p:graphicImage name="/bigLoader.gif"
style="margin-left :auto; margin-right:3.7cm;" />
</p:dialog>
<p:commandButton id="submitBtn" value="submit" ajax="false"
disabled="#{controllerBean.selectedBrands.size() lt 1}"
styleClass="btn btn-default"
onclick="PrimeFaces.monitorDownload(start, stop);">
<p:fileDownload value="#{controllerBean.file}" />
</p:commandButton>
<p:commandButton value="Add brand" styleClass="btn btn-default"
action="#{controllerBean.addBrand()}"
update=":first:brands: :first:tableGroup: :first:submitBtn:">
</p:commandButton>
I'm using Mojarra 2.2, Primefaces 5.3.
Any help much appreciated!
Filedownload is a non-ajax request, so the update attribute won't do anything.
I think you don't need to download a fake/blank file in the case of an error. You should just present the user an error msg.
I recommend that you create 2 steps to download, as your file generation process seems too slow:
First step (ajax) generates the file and saves it on the backing bean (if successful). If the generation fails, then present error message.
If the file was successfully generated (fileCreated=true), enable the download button, which will just download the generated file (ajax=false).
Remember to nullify the file variable after download to free memory.
Additionally, if you have the time, you could put a progressBar for the generation process...

enctype="multipart/form-data" does not submit data with Seam multipart-filter

Ever since I add the following config to the components.xml to customize an editor plugin, every form with enctype="multipart/form-data" in my app does not submit data.
I can't find any source saying that this is conflicting.
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*" />
I'm working with Seam 2.2.2 and Jsf 1.2
Update: I thought I could stop using forms with enctype="multipart/form-data". But I can't. I need some help and there goes more info.
First: the problem above only aplies to a4j forms and a4j commandButtons.
As I said before, I add the above web:multipart-filter config at components.xml to make this editor plugin works (which is done via apache commons ServletFileUpload).
I was taking the enctype config off the project forms to make everything work but there is one scenario that it was not possible. When I have to upload an image. But when I use url-pattern="*.seam":
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*.seam" />
Then the upload works, but the ServletFileUpload doesn't.
If I don't use any web:multipart-filter this also occurs. (image upload ok, and plugin fails)
Now it is like this:
<h:form id="editPhoto" enctype="multipart/form-data">
<div id="photoAttribute" class="attribute-relationship spacer-top-bottom">
<div class="label">
<h:outputText>#{messages['person.photo.alter']} </h:outputText>
</div>
<s:fileUpload id="photoPerson"
data="#{person.profilePhoto}"
fileName="#{person.profilePhotoName}"
fileSize="#{person.profilePhotoSize}"
accept="images/*">
</s:fileUpload>
</div>
<h:commandButton id="editPersonButtonTop"
value="#{messages['button.savePhoto']}"
action="#{personController.prepareSavePhoto(person)}"
styleClass="button btn-info"
onclick="showAjaxStatus()"/>
</h:form>
It seems I am missing some ajax config here but I can't tell what it is. And also Why can't I have both the ServletFileUpload and the image upload together?
The ServletFileUpload is from commons-fileupload-1.3.1, and it works like this:
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
processItems(items);
saveOpenAttachment();
...
private void processItems(List<FileItem> items)
{
// Process the uploaded items
Iterator<FileItem> iter = items.iterator();
while (iter.hasNext()) {
FileItem item = iter.next();
if (!item.isFormField())
{
processUploadedFile(item);
}
}
}
private void processUploadedFile(FileItem item)
{
setFileName(FilenameUtils.getName(item.getName()));
try
{
InputStream fileContent = item.getInputStream();
byte[] bytes = IOUtils.toByteArray(fileContent);
setFileData(bytes);
}
catch (IOException e)
{
e.printStackTrace();
}
setFileContentType(item.getContentType());
}
I appreciate if somebody can tell how I manage to make ServletFileUpload works with the previous components config or How to have the form to submit the data.
My components.xml:
<core:init debug="true" jndi-pattern="#jndiPattern#"/>
<web:rewrite-filter view-mapping="*.seam"/>
<web:multipart-filter create-temp-files="true"
max-request-size="1024000" url-pattern="*.seam"/>
<core:manager concurrent-request-timeout="10000"
conversation-timeout="3720000"
conversation-id-parameter="cid"
parent-conversation-id-parameter="pid"
default-flush-mode="MANUAL"/>
<persistence:managed-persistence-context name="entityManager"
auto-create="true"
persistence-unit-jndi-name="java:/SinapseEntityManagerFactory"/>
<security:identity authenticate-method="#{authenticatorContext.authenticate}" remember-me="true" />
<international:time-zone-selector time-zone-id="GMT-3"/>
<international:locale-selector name="defaultLocale" locale-string="pt" scope="application" />
<international:locale-selector locale-string="pt" />
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}"/>
</event>
<event type="org.jboss.seam.security.postAuthenticate">
<action execute="#{redirect.returnToCapturedView}"/>
</event>
<async:quartz-dispatcher/>
<cache:jboss-cache-provider configuration="ehcache.xml" />
<transaction:ejb-transaction/>
just a lucky guess, by adding
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*.seam" />
Seam registers a Multipart Filter and wrap all the request as Seam MultipartRequest and I think commons-fileupload can not process it.
for more information you can visit org.jboss.seam.web.MultipartFilter.java line 78
To solve my problem, I changed the apache-commons FileUpload solution to handle the request like Seam multipart filter does:
ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if (!(request instanceof MultipartRequest))
{
request = unwrapMultipartRequest(request);
}
if (request instanceof MultipartRequest)
{
MultipartRequest multipartRequest = (MultipartRequest) request;
String clientId = "upload";
setFileData(multipartRequest.getFileBytes(clientId));
setFileContentType(multipartRequest.getFileContentType(clientId));
setFileName(multipartRequest.getFileName(clientId));
saveOpenAttachment();
}
}
This way, I could take off the web:multipart-filter config that was breaking the other requests.

p:fileUpload inside p:dialog losing #ViewScoped values [duplicate]

This question already has answers here:
p:commandbutton action doesn't work inside p:dialog
(4 answers)
Closed 6 years ago.
I'm trying to update multiple files with <p:fileUpload>. After I upload the files, I set a List with the paths that works fine.
After that the user then has to fill some other optional information and then click on a button(to submit the form).
When the user clicks on the button all the information about the list that was done on the public void handleFileUpload(FileUploadEvent event) is lost.
I need to save the paths on the database only when the user clicks on the button, i don't understand why the values are being lost, I'm using #javax.faces.view.ViewScoped
Also when the handleFileUpload is being processed the inputs made on the screen by the user are not yet available.
JSF 2.2
CDI
PrimeFaces 5.1
I will omit some parts on the code below to avoid making it huge (if you think there's not enough info just tell me)
XHTML :
<h:form>
<!-- OMITED -->
<p:dialog>
<!-- OMITED -->
<p:fileUpload fileUploadListener="#{csrBean.handleFileUpload}"
mode="advanced"
skinSimple="true"
cancelLabel="Cancelar"
multiple="true"
auto="false"/>
<!-- OMITED -->
</p:dialog>
<!-- OMITED -->
</h:form>
The method:
public void handleFileUpload(FileUploadEvent event) {
UploadedFile file = event.getFile();
String normalize = FilenameUtils.normalize("uploads/csr/"
+ csr.getNumero() + "/" + event.getFile().getFileName());
File destino = new File(normalize);
try {
FileUtils.copyInputStreamToFile(file.getInputstream(), destino);
} catch (IOException e) {
e.printStackTrace();
}
CsrOsAnexo anexo = new CsrOsAnexo();
anexo.setCaminho(normalize);
anexo.setOs(csr.getRespostaRecente().getOs());
csr.getRespostaRecente().getOs().getAnexoList().add(anexo);
FacesMessage message = new FacesMessage("Succesful", event.getFile()
.getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, message);
}
Debugging, I can see the csr.getRespostaRecente().getOs().getAnexoList() filled with all the archives paths but as soon as the handleFileUpload() ends and I go to the method called by the commandButton, those values are gone and the values of the form are filled.
A modal dialog must have its own form.
<h:body>
...
<p:dialog>
<h:form>
...
</h:form>
</p:dialog>
</h:body>
Because, when the modal dialog is generated into HTML output, it's by JavaScript relocated to the end of HTML <body> which causes it to not be sitting in any form anymore. This relocation is necessary to guarantee compatibility with older browsers (read: IE<9) having trouble with modal overlays and z-indexes. The generated HTML DOM tree ends up to look like this (use webbrowser's dev tools to see it):
<body>
...
<form>
...
</form>
...
<div class="ui-dialog ...">
...
</div>
</body>
That the file upload apparently still worked without the form is because it autocreates a hidden iframe with therein a form to simulate the "ajax experience". However, any other action would basically lose the JSF view state and any view scoped bean would therefore get recreated.

How to display p:fileUpload invalidFileMessage in p:growl

I'm using <p:fileUpload> which is restricted to PDF only. However, the invalidFileMessage shows inside the <p:fileUpload> component. How can I show it in <p:growl> instead?
<p:fileUpload allowTypes="/(\.|\/)(pdf)$/"
invalidFileMessage="File is Invalid. Only PDF files are allowed" />
You can't handle this server side. The file type is validated at client side without hitting any code in server side. So, any suggestions which suggest to manually create FacesMessage and/or explicitly add <p:message(s)> are unthoughtful and untested.
You should use jQuery. It solves everything.
Based on the fileupload.js source code, your best bet is to listen on the fictional show event of the message container and then move the messages container to end of the form.
First extend $.show() to actually trigger the show event.
(function($) {
var originalShowFunction = $.fn.show;
$.fn.show = function() {
this.trigger("show");
return originalShowFunction.apply(this, arguments);
};
})(jQuery);
Then simply create a listener on show event which basically runs when file upload messages appear and then parse every single message and use the renderMessage() function of the <p:growl> JS API. The below example assumes that you've a <p:growl widgetVar="growl"> somewhere in the same page.
$(document).on("show", ".ui-fileupload-content>.ui-messages", function() {
$(this).children().hide().find("li").each(function(index, message) {
var $message = $(message);
PF("growl").renderMessage({
summary: $(".ui-messages-error-summary", $message).text(),
detail: $(".ui-messages-error-detail", $message).text()
});
});
});
Well add an message tag in your page something like:
<p:messages id="test" autoUpdate="true" />
And in fileupload update="#this,test" and your message will be displayed in p:messages. You can change easly in growl works the same.
Look in the primefaces showcase for more examples
Looked up an example in Primefaces showcase and found this. The actual page:
<p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
update="messages"
allowTypes="/(\.|\/)(pdf)$/"/>
<p:growl id="messages" showDetail="true"/>
And the file uploader controller class:
public void handleFileUpload(FileUploadEvent event) {
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Maybe something to keep in mind on how to display messages in Primefaces

How to do double-click prevention in JSF

We have a few search pages that run against a lot of data and take a while to complete. When a user clicks on the search button, we'd like to not allow them to submit the search result a second time.
Is there a best practice for doing "double-click" detection/prevention in JSF?
The PrimeFaces component seems like it can do what we want as it will disable the UI for a period of time between when the search button is clicked and when the search completes, but is there a more generic strategy we can use (perhaps something that isnt reliant on PrimeFaces)? Ideally, any click of the button will either be disabled or disregarded until the search completes. We dont necessarily need to disable the entire UI (as blockUI allows you to do).
If you're using solely ajax requests, you could use jsf.ajax.addOnEvent handler of the JSF JavaScript API for this. The below example will apply on all buttons of type="submit".
function handleDisableButton(data) {
if (data.source.type != "submit") {
return;
}
switch (data.status) {
case "begin":
data.source.disabled = true;
break;
case "complete":
data.source.disabled = false;
break;
}
}
jsf.ajax.addOnEvent(handleDisableButton);
Alternatively, if you need this on specific buttons only, use the onevent attribute of <f:ajax>.
<h:commandButton ...>
<f:ajax ... onevent="handleDisableButton" />
</h:commandButton>
If you also need to apply this on synchronous requests, then you need to take into account that when you disable a button during onclick, then the button's name=value pair won't be sent as request parameter and hence JSF won't be able to identify the action and invoke it. You should thus only disable it after the POST request has been sent by the browser. There is no DOM event handler for this, you'd need to use the setTimeout() hack which disables the button ~50ms after click.
<h:commandButton ... onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);" />
This is only rather brittle. It might be too short on slow clients. You'd need to increase the timeout or head to another solution.
That said, keep in mind that this only prevents double submits when submitting by a web page. This does not prevent double submits by programmatic HTTP clients like URLConnection, Apache HttpClient, Jsoup, etc. If you want to enforce uniqueness in the data model, then you should not be preventing double submits, but preventing double inserts. This can in SQL easily be achieved by putting an UNIQUE constraint on the column(s) of interest.
See also:
Pure Java/JSF implementation for double submit prevention
How to handle multiple submits before response is rendered?
You can use 'onclick' and 'oncomplete' listeners. When user click on button - disable it. When action completed - enable.
<p:commandButton id="saveBtn"
onclick="$('#saveBtn').attr('disabled',true);"
oncomplete="$('#saveBtn').attr('disabled',false);"
actionListener="#{myBean.save}" />
i came upon this question, having the same problem. The solution did not work for me - after a brief look at primefaces.js i guess they do not use jsf.ajax there anymore.
so i had to work something out myself and here is my solution, for people who also can not use the one in the answer by BalusC:
// we override the default send function of
// primeFaces here, so we can disable a button after a click
// and enable it again after
var primeFacesOriginalSendFunction = PrimeFaces.ajax.AjaxUtils.send;
PrimeFaces.ajax.AjaxUtils.send = function(cfg){
var callSource = '';
// if not string, the caller is a process - in this case we do not interfere
if(typeof(cfg.source) == 'string') {
callSource = jQuery('#' + cfg.source);
callSource.attr('disabled', 'disabled');
}
// in each case call original send
primeFacesOriginalSendFunction(cfg);
// if we disabled the button - enable it again
if(callSource != '') {
callSource.attr('disabled', 'enabled');
}
};
None of alternatives above has worked for me (I've really tried each one of them). My form was always sent twice when user double-clicked the login button. I'm working with JSF (Mojarra 2.1.6) on Glassfish 3.1.2.
Consider that it was a non-AJAX login page.
So here's the way I solved it:
define a global JavaScript var to control submition in the page header or anywhere outside your form:
var submitting = false;
set it to true when submit h:form onsubmit event is fired:
<h:form onsubmit="submitting = true">
Check the var's value on h:commandLink's click event:
<h:commandLink ... onclick="if(submitting){return false}">
This is just another simple alternative and it was tested in Chrome [Version 47.0.2526.106 (64-bit)], Mozilla Firefox (37.0.2) and Internet Explorer 11. I hope it helps someone.
For me works this way:
<h:commandLink ... onclick="jQuery(this).addClass('ui-state-disabled')">
PrimeFaces 12 and up
From PrimeFaces 12, p:commandButtons are disabled by default when they trigger an Ajax request. The button is enabled again when the Ajax request is finished.
To disable this default behavior, use disableOnAjax="false".
See a demo at: https://www.primefaces.org/showcase/ui/button/commandButton.xhtml
PrimeFaces 11 and lower
The approach by BalusC is great, but if you are using PrimeFaces you'll run into styling issues. Because some classes are not toggled, the button will not look disabled.
If you are looking for a solution which takes care of styling as well, you can replace the CommandButtonRenderer with one that disables the button on click using the button's widget to disable and enable it.
PrimeFaces Extensions 8 or up contains such a renderer. You can add this to your faces-config.xml like:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.CommandButtonRenderer</renderer-type>
<renderer-class>org.primefaces.extensions.renderer.CommandButtonSingleClickRenderer</renderer-class>
</renderer>
</render-kit>
You can see it in action in the showcase.
If you cannot or don't want to use PFE, you can add the render class to your project by getting it from:
https://github.com/primefaces-extensions/primefaces-extensions/blob/master/core/src/main/java/org/primefaces/extensions/renderer/CommandButtonSingleClickRenderer.java
Note: this still requires you to add the renderer to your faces-config.xml.
See also
How to use resolveWidgetVar before PrimeFaces 8?
very useful solution jsf-primefaces, used with facelets template spreads to other pages consumers
<f:view>
<Script language="javascript">
function checkKeyCode(evt)
{
var evt = (evt) ? evt : ((event) ? event : null);
var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
if(event.keyCode==116)
{
evt.keyCode=0;
return false
}
}
document.onkeydown=checkKeyCode;
function handleDisableButton(data) {
if (data.source.type != "submit") {
return;
}
switch (data.status) {
case "begin":
data.source.disabled = true;
break;
case "complete":
data.source.disabled = false;
break;
}
}
jsf.ajax.addOnEvent(handleDisableButton);
</Script>
</f:view>
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="./resources/css/default.css" rel="stylesheet" type="text/css" />
<link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
<title>infoColegios - Bienvenido al Sistema de Administracion</title>
</h:head>
<h:body onload="#{login.validaDatos(e)}">
<p:layout fullPage="true">
<p:layoutUnit position="north" size="120" resizable="false" closable="false" collapsible="false">
<p:graphicImage value="./resources/images/descarga.jpg" title="imagen"/>
<h:outputText value="InfoColegios - Bienvenido al Sistema de Administracion" style="font-size: large; color: #045491; font-weight: bold"></h:outputText>
</p:layoutUnit>
<p:layoutUnit position="west" size="175" header="Nuestra InstituciĆ³n" collapsible="true" effect="drop" effectSpeed="">
<p:menu>
<p:submenu>
<p:menuitem value="Quienes Somos" url="http://www.primefaces.org/showcase-labs/ui/home.jsf" />
</p:submenu>
</p:menu>
</p:layoutUnit>
<p:layoutUnit position="center">
<ui:insert name="content">Content</ui:insert>
</p:layoutUnit>
</p:layout>
</h:body>
Did a simple work with hide and show, works well with element having input type submit Jquery
$(":submit").click(function (event) {
// add exception to class skipDisable
if (!$(this).hasClass("skipDisable")) {
$(this).hide();
$(this).after("<input type='submit' value='"+$(this).val()+"' disabled='disabled'/>");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<form>
<input type="submit" value="hello">
</form>
I addressed this issue by simply hiding the button after clicking it:
<p:commandButton... onclick="jQuery(this).css('visibility','hidden')" />

Resources