Display image files uploaded in a Multiple File control

From pmusers
Jump to: navigation, search
DynaForm: Upload-image-files.json
Tested: PM 3.2.1 Community, Debian 8.4, PHP 5.6.20, Firefox 51

MultipleFile controls, which are labeled as "uploadfile" in the Dynaform Designer, allow a variable number of files to be uploaded. MultipleFile controls are convenient because they allow users to download and delete the files after they are uploaded. However, they do not provide an easy way to view the uploaded files.

Custom JavaScript code can be added to a DynaForm to display image files below the MultipleFile control in a panel control. This allows the user to view the file and decide whether the keep the file or delete it. The trick is to update the images in the panel every time the user changes the list of uploaded files.

In order to interact with MultipleFile controls, it is first necessary to understand how ProcessMaker stores information about uploaded files in DynaForms. ProcessMaker uses Backbone.js to store the model information about DynaForm fields and the attributes for each field is available in an object at:
getFieldById("field-id").model.attributes
or
$("#field-id").getInfo()

For example, the following form has a MultipleFile control with two uploaded files:

ShowImagesButtonInMultipleFileForm.png

The fileCollection object is large and complicated, but here is a simplified representation of the more useful information in the object about the two uploaded files stored in getFieldById('imageFiles').model.attributes.fileCollection:

{
  length: 2,
  models: [
    {
      attributes {
        appDocUid: "2489364075ab54e0de17a28021324630",
        file: {
          lastModified: 1513915201000,
          lastModifiedDate: Date 2017-12-22T04:00:01.000Z,
          name: "ForumCaptcha.png", 
          size: 13745,  
          type: "image/png"
        }
        index: 0,
        isValid: true,
        mode: "edit",
        percentage: 100,
        size: "16",
        sizeUnity: "MB",
        urlBase: "{server}/sys{ws}/en/{skin}/cases/cases_ShowDocument?a={docUID}&v=1",
        version: 1
      },
      getDataFileLink() //in version 3.2.1 and earlier
      getData()         //in version 3.2.2 and later
      getLinkDownload() //in version 3.2.2 and later
      
    },
    {      
      attributes {
        appDocUid: "8001226465ab54e0e274224073266424",
        file: {
          lastModified: 1513124784000,
          lastModifiedDate: Date 2017-12-13T00:26:24.000Z,
          name: "GoogleMap.png", 
          size: 287173,
          type: "image/png"
        }
        index: 1,
        isValid: true,
        mode: "edit",
        percentage: 100,
        size: "16",
        sizeUnity: "MB",
        urlBase: "{server}/sys{ws}/en/{skin}/cases/cases_ShowDocument?a={docUID}&v=1",
        version: 1
      },
      getDataFileLink() //in version 3.2.1 and earlier
      getData()         //in version 3.2.2 and later
      getLinkDownload() //in version 3.2.2 and later
    }
  ],
}

fileCollection.length is the number of uploaded files and fileCollection.models is a array of objects holding information about each uploaded file. The unique ID assigned to the first uploaded file is found in fileCollection.models[0].attributes.appDocUid and its filename is found in fileCollection.models[0].attributes.file.name. To check whether a file is allowed to be uploaded because it has the correct extension and is below the maximum allowed file size, check that the fileCollection.models[0].attributes.isValid is set to true. To check whether the file has finished uploading, check whether the fileCollection.models[0].attributes.percentage is set to 100.

The easiest way to obtain information about an uploaded file is to use the fileCollection.models[index].getDataFileLink() function. For example, getFieldById('imageFiles').model.attributes.fileCollection.models[0].getDataFileLink() would return the following object in version 3.2.1 and earlier:

{  
  ext: "png",
  href: "http://localhost:321/sysworkflow/en/neoclassic/cases/cases_ShowDocument?a=2489364075ab54e0de17a28021324630&v=1",
  icon: "file-mage-o",
  name: "ForumCaptcha.png"
}

The href is particularly useful because it generates the full URL to access the file. Using this file information, JavaScript code can be added to periodically check whether new files have been added to the DynaForm and then display them in <img> tags inside a panel.

Note: In version 3.2.2 and later, the getDataFileLink() function no longer exists, but fileCollection.models[x].getLinkDownload() can be used to get the URL to download the file.

For example, create the following DynaForm to upload image files:

MultipleFileForm.png

This form has the following fields:

  • An "Image Files" MultipleFile with the ID "imageFiles",
  • A "Show Images" button with the ID "showImage",
  • A panel with the ID "imagesPanel" which has no content,
  • A "Submit" button with the ID "submitButton"

When the "Show Images" button is clicked, it will display the "imagesPanel" and change the label of the button to "Hide Images". The next time it is clicked, it will hide the "imagesPanel" and change the label back to "Show Images". This way the user can easily choose whether to view the images in the uploaded files or not.

ShowImagesMultipleFileForm.png

The following JavaScript code is added to the DynaForm in order to implement this functionality:

var aPreviousFileUids = []; //array holding UIDs of uploaded files
var panelWidth = $("#imagesPanel").find("div.panel-body").width();

function showAnyImages() {
    //check if the list of uploaded files has changed since last showAnyImages():
    var oFiles = getFieldById('imageFiles').model.attributes.fileCollection.models;
    aCurrentFileUids = [];
    
    for (i=0; i < oFiles.length; i++) {
      aCurrentFileUids.push( oFiles[i].attributes.appDocUid );
    }
    
    //if no change in file list:
    if (JSON.stringify(aCurrentFileUids) == JSON.stringify(aPreviousFileUids)) {
      return;
    }
    
    //update the file images
    aPreviousFileUids = aCurrentFileUids;
	var imagesHtml = '';
  
    for (i=0; i < oFiles.length; i++) {
      var oFileInfo = oFiles[i].getDataFileLink();
      if (oFileInfo.ext=='png' || oFileInfo.ext=='jpeg' || oFileInfo.ext=='jpg') {
        imagesHtml += '<p>'+oFileInfo.name+':<br><img src="'+oFileInfo.href+'"></p>'
      }
    }
  
    //set max-width of the images in the panel:
    var panel = $("#imagesPanel").find("div.panel-body");
    panel.html(imagesHtml); 
    var aImgs = panel.find("img").each(function() {
        $(this).css("max-width", panelWidth);
    });
}

function clickShowImages() {
  if ($("#showImages").getValue() == 'Show Images') {
    $("#showImages").setValue('Hide Images');
    $("#imagesPanel").show();
    showAnyImages();
  }
  else { //if 'Hide Images':
    $("#showImages").setValue('Show Images');
    $("#imagesPanel").hide();
  }
  //reset onclick handler because setValue() resets properties of button
  $("#showImages").find("button").click(clickShowImages);
}

//Execute when Dynaform loads:
$("#showImages").find("button").click(clickShowImages);
$("#imagesPanel").hide();
setInterval("showAnyImages()", 2000);

When the user clicks on the "showImages" button, it will execute the clickShowImages() function, which toggles the button's label from "Show Images" to "Hide Images" and displays the "imagesPanel" below the MultipleFile control.

It also calls the showAnyImages() function, which loops through the array of uploaded files in the fileCollection.models array. If the uploaded file has the extension of an image file, then the function adds an <img> element to the panel.

In addition, the code uses setInterval() to set a timer which executes the showAnyImages() function every two seconds. It checks whether the list of uploaded files has changed in the last two seconds. If so, then it refreshes the list of images in the panel.

To test out this code, download and import the Upload-image-files.json DynaForm (right click on link and select Save link as).