Attaching files

We would like to attach one or more files to each contact. For example, we can attach some image, or the CV, or other useful information about the contact.

By implementing this feature, we are going to introduce three new components and explain how to use them in a productive way.

Creating the wizard

Let's start creating the upload file wizard this is a simple two-page wizard that enables the user to upload and then review files, eventually adding a note to each one.

Let's create the /view/main/uploadFiles/ directory and, inside it, an empty file called wizardFirstStepUploadFiles.xhtml (that will be the first step page of our wizard) with the following content:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a="http://richfaces.org/a4j">
<!-- my code -->
</ui:composition>

Now we have to insert (replacing the<!-- my code --> comment) the form with the RichFaces file upload:

<h:form>
<h:outputText value="#{messages['selectFilesToUpload']}" />
<rich:fileUpload acceptedTypes="gif,jpg,png,pdf,doc,xls" allowFlash="auto" autoclear="false" maxFilesQuantity="10" immediateUpload="true" fileUploadListener="#{filesUploadHelper.listener}">
<a:support event="onuploadcomplete" reRender="nextBtn" />
</rich:fileUpload>
</h:form>

The working way is very intuitive for every file we upload, a listener method is called, so the application can manage the uploaded file.

The other attributes we have set are easy to understand, but a special mention goes to the allowFlash attribute, which permits the enabling of a Flash uploading panel component. If the Flash plugin is enabled, it allows the user to select more than one file to upload at a time.

Where the uploaded files are stored depends on createTempFile, an init-param set in web.xml file. By default, its value is true, and the files are stored in a temporary folder. If the parameter is set to false, then the uploaded files will be kept in the RAM (it is a better mode if you have uploaded small files).

In order to change the parameter value, you have to open the web.xml file and add this code:

<init-param> <param-name>createTempFiles</param-name> <param-value>false</param-value> </init-param>

Before creating the bean, we have to define the path where the uploaded files will be saved, and we can use the uiOptions component for that. Just open the UIOption class and add the following property:

private String fileSavePath;
public String getFileSavePath() {
return fileSavePath;
}
public void setFileSavePath(String fileSavePath) {
this.fileSavePath = fileSavePath;
}

Now we have to configure it in the components.xml file. Let's open it and add a property to the uiOption component initialization:

<property name="fileSavePath">/my/file/path/</property>

For security reasons, it's highly recommended to put the file path outside the application we will see how to enable the access through the application later on.

Now, we need the Seam component that manages the file upload process (filesUploadHelper). Let's create a new class called FilesUploadHelper inside the book.richfaces.advcm.modules.main.files package:

@Name("filesUploadHelper")
@Scope(ScopeType.CONVERSATION)
public class FilesUploadHelper       {     @In(create = true)
EntityManager entityManager;

@In(required = true)
HomeSelectedContactHelper homeSelectedContactHelper;

@In
UIOptions uiOptions;
}

Besides the entityManager, we need the uiOptions component because of the fileSavePath property. We also need homeSelectedContactHelper to get the selected contact to associate files with.

Now let's insert the listener() method:

public void listener(UploadEvent event) throws Exception {
UploadItem item = event.getUploadItem();
// Creating the instance
ContactFile newFile = new ContactFile(homeSelectedContactHelper. getSelectedContact(), item.getFileName(), item.getContentType());
// Persisting it into the database
entityManager.persist(newFile);
// Copying the files into the disk using the new id
copyFile(new FileInputStream(item.getFile()),
new FileOutputStream(uiOptions.getFileSavePath()
+ newFile.getId()));
}

As you can see, this method gets the UploadItem instance of the uploaded file, creates the association with the selected contact, and persists it into the database. After that it copies the temporary file to our preferred position, the following copyFile method is used to this:

private void copyFile(FileInputStream sourceStream, FileOutputStream destinationStream) throws IOException {
FileChannel inChannel = sourceStream.getChannel();
FileChannel outChannel = destinationStream.getChannel();
try {
inChannel.transferTo(0, inChannel.size(),
outChannel);
}
catch (IOException e) {
throw e;
}
finally {
if (inChannel != null) {
try {
inChannel.close();
} catch (IOException ioe) { }
}
if (outChannel != null) outChannel.close();
}
}

This is an almost general bean that you can use in your application by just changing the database code.

The file review step

The wizard has two steps we've seen the first one that permits the user to select and upload files. The second one is used to review the files and to eventually add a note to each one.

Before creating the second page, let's add the navigation code to the first one, after the</rich:fileUpload> closing tag:

<a:commandLink id="nextBtn" action="next" style="float:left;" styleClass="image-command-link">
<h:graphicImage value="/img/next.png" />
<h:outputText value="#{messages['next']}" />
</a:commandLink>

This button permits the user to navigate to the next page using the JSF navigation rules from the next outcome. Let's open the faces-config.xml file and add the following code after the</application> closing tag:

<navigation-rule>
<from-view-id>/main/uploadFiles/wizardFirstStepUploadFiles.xhtml
</from-view-id>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/main/uploadFiles/wizardSecondStepUploadFiles.xhtml
</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id> /main/uploadFiles/wizardSecondStepUploadFiles.xhtml
</from-view-id>
<navigation-case>
<from-outcome>previous</from-outcome>
<to-view-id> /main/uploadFiles/wizardFirstStepUploadFiles.xhtml </to-view-id>
</navigation-case>
</navigation-rule>

This is standard JSF that permits navigation to the second step page in case of the next outcome, and going back to the first one in case of the previous outcome.

The screenshot of the first step of the wizard is as follows:

The file review step

Now let's create an empty file (using the previously shown template) again called wizardSecondStepUploadFiles.xhtml and add the following code:

<h:form>
<ui:include src="showCurrentContactFiles.xhtml">
<ui:param name="edit" value="true"/>
<ui:param name="columns" value="2"/>
</ui:include>
<a:commandLink ajaxSingle="true" action="previous" reRender="uploadImagesWizard" style="float:left;" styleClass="image-command-link">
<h:graphicImage value="/img/previous.png"/>
<h:outputText value="#{messages['previous']}"/>
</a:commandLink>
</h:form>

You can notice that there is an included file (using the ui:include Facelets tag) with two passed parameters that's because we will reuse the code to show the files' list for another feature (you will see it later).

The other component to go back to the first step is commandLink.

Now, let's create the showCurrentContactFiles.xhtml file using the empty template and add the following code:

<a:outputPanel style="width: 500px; height: 400px; overflow: auto;" layout="block">
<rich:dataGrid value="#{filesListHelper.files}" var="file" columns="#{columns}">
<a:outputPanel layout="block" rendered="#{edit==true}" style="text-align: center;">
<h:outputText value="#{file.fileName}"/>
<br/><br/>
<h:inputTextarea value="#{file.description}" style="width: 150px; height: 50px;"/>
</a:outputPanel>
</rich:dataGrid>
<h:outputText value="#{messages['noFilesFound']}" rendered="#{empty filesListHelper.files}"/>
</a:outputPanel>

Here, we have rich:dataGrid with the number of columns passed as a Facelets parameter (columns) that renders an edit box for every uploaded file with h:inputTextarea to let the user add the file description.

As you can see (the highlighted code), this panel is shown only when the Facelets parameter edit is set to true. We will later add another panel to manage the case when edit is set to false.

This is the result with some associated files:

The file review step

Creating the modal panel

We want to show the upload wizard inside a modal panel. Therefore, let's start creating the code to show it.

Let's create a new file (with the empty template we've seen) inside the /view/main/uploadFiles/ folder, called uploadFilesModalPanel.xhtml, and put the following code inside the ui:component tag:

<rich:modalPanel id="uploadFilesMP"
minHeight="300"
minWidth="350"
autosized="true"
moveable="true"
resizeable="false"> <f:facet name="header">
<h:outputText value="#{messages['uploadNewFiles']}"/> </f:facet>
<!-- my code -->
</rich:modalPanel>

Here, I defined the rich:modalPanel component setting the id and some attributes like minWidth, minHeight, autosized, movable, and resizable. Also, I have added the f:facet tag to customize the panel header.

This is a sample of what we have done:

Creating the modal panel

The code to open the modal panel is very simple.

Let's open the contactEdit.xhtml file and add the new rich:toolBar group to the latest rich:toolBar component:

<rich:toolBarGroup location="right">
<a:commandLink
onclick="#{rich:component('uploadFilesMP')}.show();"
styleClass="image-command-link">
<h:graphicImage value="/img/upload.png"/>
<h:outputText value="#{messages['uploadFiles']}"/>
</a:commandLink>
</rich:toolBarGroup>

The highlighted line is the one that does the trick to close the panel (for example, by using a button inside it). We can use the same code, but call the JavaScript hide() method instead of show(), very simple!

Remember that to make it work, we have to include the file into our page, so let's open the home.xhtml file and add the following code at the end, after the</h:panelGrid> closing tag:

<ui:include src="main/uploadFiles/uploadFilesModalPanel.xhtml" />

Control components without JavaScript

Another way of controlling the rich:modalPanel component (and the other components that have JavaScript API, which permits controlling them) is to use the rich:componentControl component. It allows, in fact, to call JavaScript functions of a component after a specific event.

Let's make an example of use by calling the show() function of our modal panel using rich:componentControl:

<a:commandLink styleClass="image-command-link">
<h:graphicImage value="/img/upload.png"/>
<h:outputText value="#{messages['uploadFiles']}"/>
<rich:componentControl for="uploadFilesMP" event="onclick" operation="show"/>
</a:commandLink>

By inserting the component inside the a:commandLink, we automatically attached to it however, it is also possible to specify the component to attach to using the attachTo attribute:

<a:commandLink id="showPanelBtn" styleClass="image-command-link">
<h:graphicImage value="/img/upload.png"/>
<h:outputText value="#{messages['uploadFiles']}"/>
</a:commandLink>
<rich:componentControl for="uploadFilesMP" attachTo="showPanelBtn" event="onclick" operation="show"/>

Inserting the wizard inside the modal panel

We are now ready to insert the wizard inside the modal panel and enable Ajax navigation using standard navigation rules. We are going to use the a:include component to make this possible!

Let's open the /view/main/uploadFiles/uploadFilesModalPanel.xhtml again and replace the<!-- my code --> comment with the code to include (the Ajax way!) the first step of the wizard:

<a:outputPanel id="uploadFilesWizard">
<a:include ajaxRendered="true" viewId="/main/uploadFiles/wizardFirstStepUploadFiles.xhtml"/>
</a:outputPanel>

Now, let's add the close button into the first wizard step (to the /view/main/uploadFiles/wizardFirstStepUploadFiles.xhtml file):

<a:commandLink onclick="#{rich:component('uploadFilesMP')}.hide();"
style="float:right;" styleClass="image-command-link">
<h:graphicImage value="/img/close.png" />
<h:outputText value="#{messages['close']}" />
</a:commandLink>

This is just the standard code we've seen for closing a modal panel (notice the call to the hide() method).

In the second step, we also want to save the modification before closing the panel, so the code to add (to the /view/main/uploadFiles/wizardSecondStepUploadFiles.xhtml file) is slightly different:

<a:commandLink
action="#{filesListHelper.updateList}"
oncomplete="#{rich:component('uploadFilesMP')}.hide();"
style="float:right;" styleClass="image-command-link">
<h:graphicImage value="/img/close.png"/>
<h:outputText value="#{messages['finish']}"/>
</a:commandLink>

Here, the framework first calls the filesListHelper.updateList action method and then (oncomplete) closes the panel.

Here are the two screenshots of the steps inside the modal panel:

Inserting the wizard inside the modal panel
Inserting the wizard inside the modal panel

Finishing the file upload feature

The last feature we are going to add is the Files panel to show (or edit if in edit mode) the files associated to a contact.

In order to do that, we are going to use what we've seen uptil now, so let's start creating a new XHTML file with a modal panel called showFilesModalPanel.xhtml containing the following code:

<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<rich:modalPanel id="showFilesMP" minHeight="400" minWidth="500" autosized="true"
moveable="true" resizeable="false">
<f:facet name="header">
<h:outputText value="#{messages['showFiles']}"/>
</f:facet>
<h:form>
file attachmentfinishing with<a:outputPanel id="showFilesWizard">
<ui:include src="showCurrentContactFiles.xhtml">
<ui:param name="edit" value="#{homeSelectedContactHelper. selectedContactEditing}"/>
<ui:param name="columns" value="3"/>
</ui:include>
<br/>
<h:panelGroup style="float:right;">
<a:commandLink action="#{filesListHelper.updateList}" oncomplete="#{rich:component('showFilesMP')}.hide();" rendered="#{homeSelectedContactHelper. selectedContactEditing}"
styleClass="image-command-link">
<h:graphicImage value="/img/files.png"/>
<h:outputText value="#{messages['save']}"/>
</a:commandLink>
<rich:spacer width="5"/>
<a:commandLink action="previous" onclick="#{rich:component('showFilesMP')}.hide();"
styleClass="image-command-link">
<h:graphicImage value="/img/close.png"/>
<h:outputText value="#{messages['close']}"/>
</a:commandLink>
</h:panelGroup>
</a:outputPanel>
</h:form>
</rich:modalPanel>
</ui:component>

Here, we've created a modal panel that includes the showCurrentContactFiles.xhtml (the one we created) file to show the files data grid. This time the edit Facelets parameter depends on homeSelectedContactHelper.selectedContactEditing property (you can see it highlighted in the code) so, it will be set to true in edit mode, but false otherwise.

After the ui:include, we have two buttons one is the Save button (shown only in edit mode) and the other is the Close button.

It's time to implement the panel rendered when not in the edit mode inside the showCurrentContactFiles.xhtml file let's open it again and add the following code right after the<rich:dataGrid ..> tag:

<a:outputPanel layout="block" rendered="#{edit==false}" style="text-align: center;">
<h:outputText value="#{file.fileName}" style="font-weight: bold;"/>
<h:outputText value="#{file.fileName}" style="font-weight: bold;"/>
<br/><br/>
<h:outputText value="#{file.description}" escape="false"/>
<br/><br/>
<s:link action="#{fileDownloadHelper.download}" styleClass="image-command-link">
<f:param name="cid" value="#{file.id}"/>
<h:graphicImage value="/img/download.png"/>
<h:outputText value="#{messages['download']}"/>
</s:link>
</a:outputPanel>

This panel is rendered when the edit Facelets parameter is set to false.

The download method of the FileDownloadHelper bean we implemented is the following:

public void download() {
ContactFile contactFile = entityManager.find(ContactFile.class, contactFileId);
try {
// Get the file
File file = new File(appOptions.getFileSavePath() + contactFile.getId());
long fileLength = file.length();
// Create the stream
FileInputStream fileIS = new FileInputStream(file);
// Get the data
byte fileContent[] = new byte[(int) fileLength];
fileIS.read(fileContent);
// Stream the content
FacesContext facesContext = FacesContext.getCurrentInstance();
if (!facesContext.getResponseComplete()) {
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.setContentType(contactFile.getFileType());
response.setContentLength((int) fileLength);
response.setHeader("Content-disposition", "attachment; filename=" + contactFile.getFileName());
ServletOutputStream out;
out = response.getOutputStream();
out.write(fileContent);
out.flush();
facesContext.responseComplete();
}
} catch (IOException e) {
e.printStackTrace();
file attachmentfinishing with}
}

It is useful for the download of small files (less than Integer.MAX_VALUE); for big ones it's better you use a servlet or a custom Seam resource.

The last step is adding the code to open the modal panel, both in view and in edit mode.

Let's open the contactView.xhtml file and add another rich:toolBarGroup tag inside rich:toolBar:

<rich:toolBarGroup location="right">
<a:commandLink oncomplete="#{rich:component('showFilesMP')}.show();" ajaxSingle="true" reRender="showFilesPanel" styleClass="image-command-link">
<f:setPropertyActionListener value="#{null}" target="#{filesListHelper.files}"/>
<h:graphicImage value="/img/files.png"/>
<h:outputText value="#{messages['files']}"/>
</a:commandLink>
</rich:toolBarGroup>

The commandLink will force the re-read of the files list (by setting it to null) and will re-render showFilesPanel to synchronize the changes after that the modal panel will be opened by the oncomplete JavaScript code.

Here is a screenshot of the final rich:toolBar in view mode:

Finishing the file upload feature

The same commandLink must be added inside the contactEdit.xhtml file, near the Upload button, as it appears in the next screenshot:

Finishing the file upload feature

Our new panel is ready; let's see how it looks in view mode:

Finishing the file upload feature

And in edit mode it appears as follows:

Finishing the file upload feature
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.191.86