24 Nov 2013

Replacing existing file with uploaded file from FileUpload Control.

I have been creating an XPages application that contains 1 icon which is uploaded using the fileUpload Control and is displayed using an image control. Simple and straight forward.

I also decided that I wanted the image to always have the same file name for ease of later use; this was the result:
<xp:fileUpload id="fupIcon" value="#{docApplicationDetails.Icon}"
    useUploadname="false" filename="icon.jpg">
    <xp:eventHandler event="onchange" submit="true"
        refreshMode="complete">
    </xp:eventHandler>
</xp:fileUpload>
<xp:image id="imgIcon">
    <xp:this.url><![CDATA[#{javascript:try{
    if(docApplicationDetails.getAttachmentList("Icon").length > 0){
        return docApplicationDetails.getAttachmentList("Icon")[0].getHref();
    }
}catch(e){
    return null;
}}]]></xp:this.url>
</xp:image>
 The above was great; it uploaded the image and displayed it. However if you upload another image it still displays the first image which is exactly what i had told it to do:
docApplicationDetails.getAttachmentList("Icon")[0].getHref()

To get 1 upload only I could have used this snippet posted by Sven Hasselbach
Unfortunately, this snippet was not suitable for me as it involves saving the document so I decided to use this:

if(docApplicationDetails.getAttachmentList("Icon").size() > 1{
    //remove the previous attachment
    var i = docApplicationDetails.getAttachmentList("Icon").size()-1;
    docApplicationDetails.getAttachmentList("Icon").remove(i);
}
This was also not suitable as the first image that gets uploaded gets named 'icon.jpg' but the next image is named 'icon_1.jpg'.

Finally, I decided the best approach would be to remove the first attachment before the second is uploaded and the way I decided to do this was using a validator. The validator is ran in the page lifecycle before the data model is updated (file is uploaded to the document).
This was the final result:
XPage:
<xp:fileUpload id="fupIcon" value="#{docApplicationDetails.Icon}"
    useUploadname="false" filename="icon">
    <xp:this.validator><![CDATA[#{pageController.defaultDocumentEvents.validateIconUpload}]]></xp:this.validator>
    <xp:eventHandler event="onchange" submit="true"
        refreshMode="complete">
    </xp:eventHandler>
</xp:fileUpload>
<xp:image id="imgIcon">
    <xp:this.url><![CDATA[#{javascript:try{
    if(docApplicationDetails.getAttachmentList("Icon").length > 0){
        return docApplicationDetails.getAttachmentList("Icon")[0].getHref();
    }
}catch(e){
    return null;
}}]]></xp:this.url>
</xp:image>
Validator:
public void validateIconUpload(
        FacesContext facesContext, UIComponent component, Object value) throws ValidatorException {
    //using validation to remove existing upload before allow new one
    DebugToolbar.get().info("Validating Icon Upload");

    try {
        //check if there is already an uploaded attachment
        DominoDocument docApplicationDetails = JSFUtil.getXspDocument("docApplicationDetails");
        if(docApplicationDetails.getAttachmentList("Icon").size() > 0){
            //remove the existing attachment
            docApplicationDetails.removeAllAttachments("Icon");
        }
    } catch (NotesException e) {
        FacesMessage message = new FacesMessage("An Error occurred whilst uploading the Icon");
        // Throw exception so that it prevents icon being uploaded
        throw new ValidatorException(message);
    }
}

BxSlider