Extending Autonomy’s Liquid Office – My new blog series

22 01 2012

During the last 2 years absence from my blog I have been very busy working on a large project for delivering a common electronic forms solution. Throughout this time I have gathered extensive knowledge in extending Autonomy’s Liquid Office which is the underlying software that powers this eForms solution. Given the lack of technical articles and blogs related to this product I have decided to create a series of blog articles.

In my first article of this series I will be explaining how to allow an end user to download a PDF copy of the form being filled using a different form template. This may come handy in various ways such as but not limited to:

  1. Printing a PDF copy without the submit buttons or containing a subset of the form fields
  2. Downloading a PDF copy to be digitally signed by the end user using an X.509 certificate and then uploaded as a form attachment (maybe this would be an interesting topic to expand in a future blog post).

Steps to download a PDF copy of the form being filled

The following sequence diagram shows the high level steps involved in generating and downloading a PDF copy using a different form template from the one being used.

Form Client Web Browser Forms LiquidOffice Server Forms Web Server
1. The client presses the ‘Download PDF’ button which triggers a JavaScript which triggers the LiquidOffice AJAX call. 2. The LiquidOffice server receives the HTTP request triggered by the AJAX call and executes the CSForm_OnValidateLookupserver-side function.

3. The OnValidateLookup calls the PDFHelper.generatePDF method to generate a PDF based on the specified settings and returns the URL to download the PDF as the HTTP response to the AJAX call.

4. The client receives the server response containing the URL to download the PDF.

5. The client makes an HTTP request to download the PDF.

7. The client receives the PDF document and chooses to either open or save the file.

6. The custom Java Servlet receives the request to download the PDF and streams the PDF to the client.

Form client side code

When the user presses the PDFDownload button the btnPDFDownload_OnButtonClick() function is called. This will invoke the pdfDownload function which triggers the form to perform an AJAX call to the server using the btnPDFDownload as its target. If successful the AJAX call returns with the URL to download the PDF copy, otherwise it either returns with a status message or an exception which you will need to modify to handle the behaviour according to your needs.

function btnPDFDownload_OnButtonClick() {
	pdfDownload(CSEvent.getTarget().getName());
}

// Downloads a PDF copy
function pdfDownload(target) {
	// Trigger a LiquidOffice OnValidateLookup AJAX call to generate the PDF
	ajaxCall(target);

	// Get the server side response URL
	var url = CSForm.getResponseURL();

	// Make sure that the return URL is not empty or null
	if (url !== undefined && url !== "") {

		// Navigate to the PDF Download URL
		window.open(url);
	}
};

// Triggers a LiquidOffice OnValidateLookup AJAX using the specified target
function ajaxCall(target) {
	try {

		// Get the URL to submit the AJAX request
		var urlstr = CSExt.getFldVal("DFS__SubmitURL");

		// Set the LiquidOffice Action and Field values
		CSExt.setFldVal("DFS__Action", "DBCommandsOnTabOut");
		CSExt.setFldVal("DFS__Field", target);

		// Execute the AJAX call
		if (cs_ajax_submit(urlstr, "") === 0) {

			// Check whether the response contains a status message
			if (CSExt.getFldVal("DFS__StatusMsg") !== "") {

				// Display the status message to the client
				CSClient.alert(CSExt.getFldVal("DFS__StatusMsg"));
			}
		}
	} catch (err) {
		trace("ajaxCall() Exception");
		trace(err.message);
	}
};

function trace(s) {
	try {
		// Log message to console
		console.log(s);
	} catch (e) { }
}

Form server side code
The AJAX call triggers the CSForm_OnValidateLookup() server side function. This function verifies that the target set was in fact the btnPDFDownload and calls the getPDFDownloadURL() function which, based on the specified settings, will return the URL to download the generate PDF. The getPDFDownloadURL() function has 2 hardcoded variables called formName and formPath which you will need to change to point to the required PDF Template.

importPackage(Packages.java.io);
importPackage(Packages.com.wordpress.alanattard.liquidoffice);

function CSForm_OnValidateLookup() {
	try {
		var target = CSEvent.getTarget();

		if (CSEvent.getAction() != "Submit" && target != null) {
			// Make sure that the target that triggered
			// this AJAX call was the PDF download button
			if (target.getName().indexOf("btnPDFDownload") == 0) {
				// Get the URL for the generated PDF
				getPDFDownloadURL();
			}
		}
	}
	catch (e) {
		// Display the message to the user
		CSForm.setStatusMsg(e.message);
	}
}

// Gets the URL of a PDF generated and sets it in the ResponseURL hidden field
function getPDFDownloadURL() {
	var url, formName, formPath, param, pdfPath;

	// The formName and formPath for the form template to use to generate the PDF
	// ***** These values must be changed according to the publishing path *****
	formName = "PDFTemplate";
	formPath = "Forms";

	// Call the generatePDF method to generate the PDF based on the provided settings
	pdfPath = PDFHelper.generatePDF(CSForm, CSDoc, formName, formPath);

	// Check that the generatePDF method returned the PDF filename
	if (pdfPath !== "") {
		// Construct the QueryString to download the required PDF file
		param = "fileName=" + pdfPath + "&formName=" + formName + "&type=save";

		// Construct the full URL to the PDF download servlet
		url = "/xmlserver/PDFDownload?" + param;

		// Instruct the client to navigate to the PDF download URL
		CSForm.setResponseURL(url);
	}
}

Java PDF Helper Class
The PDFHelper class provides a method called generatePDF which generates a PDF based on the provided parameter values. This method starts by retrieving the FormGUID of the specified PDF Form Template and then uses the FormGUID to get all the form properties. The value of the form document envelope properties DFS__FormGUID, DFS__FormRev and LF__FormID are changed to the corresponding PDF Form Template values. The updated document envelope is used to call the ManagePDF.getFlatPDF method which returns the full path for the generated PDF. The directory information is stripped for security reasons given that the PDF path value will be returned to the end user.

public static String generatePDF(JSForm form, Document doc, String formName, String path) {

	String pdfPath = null;

	// Get an instance of the Liquid Office logger
	Log log = LogFactory.getLog();

	try {
		// Get the Form GUID for the PDF template
		String formGUID = FormManager.getFormGUID("/" + path, formName);

		// Get the Form properties
		Map <String, String> formMap = FormManager.getFormMap(null, formGUID, null);
    
		// Replace the control details to point to the required PDF template
		Envelope.setCommandParamValue(doc, "DFS__FormGUID", formGUID);
		Envelope.setCommandParamValue(doc, "DFS__FormRev", (String)formMap.get("DFS__FORMREV"));
		Envelope.setCommandParamValue(doc, "LF__FormID", (String)formMap.get("DFS__FORMID"));

		// Generate the PDF
		pdfPath = ManagePDF.getFlatPDF(doc, null, null, null, false, false, false, true);

		if (pdfPath != null) {
			// Remove the directory path from the path for security reason
			pdfPath = pdfPath.replace(DirectoryManager.getFormsAttachDirAbsolutePath(), "");
		}
	}
	catch (Exception e)
	{
		// Log the exception in LiquidOffice
		log.error(e);
	}
    
	return pdfPath;
}

Java Servlet code
The PDFDownload servlet only supports the HTTP GET method but may easily be modified to also support the HTTP POST method. The servlet expects the following request parameters:

  • fileName (mandatory)
    The filename of the PDF to be download
  • formName (mandatory)
    The file name to be used when downloading the PDF
  • type (optional)
    If ‘save’ the file is streamed as an attachment, otherwise as inline
    • The servlet will simply rebuild the full path for the PDF and stream the content to the client.

      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          
      	FileInputStream inp = null; 
      	ServletOutputStream out = null;
      	BufferedInputStream buf = null;
              int readBytes = 0;
      
      	// Get an instance of the Liquid Office logger
      	Log log = LogFactory.getLog();
      
      	try
      	{
      		// Get the QueryString parameters sent by the caller
      		String fileName = request.getParameter("fileName");
      		String formName = request.getParameter("formName");
      		String type = request.getParameter("type");
                  
      		// Validated the fileName QueryString
      		if (fileName == null || fileName.length() == 0) {
      			// Log the warning in LiquidOffice
      			log.warn("The PDFDownloadServlet query string 'fileName' must be provided.");
                  	
      			return;
      		}
      
      		// Validated the formName QueryString
      		if (formName == null || formName.length() == 0) {
      			// Log the warning in LiquidOffice
      			log.warn("The PDFDownloadServlet query string 'formName' must be provided.");
      
      			return;
      		}
                  
      		// Create the full path for the requested PDF document
      		String pdfFullPath = DirectoryManager.getFormsAttachDirAbsolutePath() + fileName;
      
      		// Create a File instance based on the PDF path
      		File pdfFile = new File(pdfFullPath);
                  
      		// Validated that the requested PDF exists
      		if (pdfFile.exists() == false) {
      			// Log the warning in LiquidOffice
      			log.warn("The requested PDF file does not exist.");
      
      			return;
      		}
      
      		// Construct the download file name
      		String downloadFileName = formName.replace(" ", "") + ".pdf";
                  
      		// Set the HTTP Response Content Type
      		response.setContentType("application/pdf");
      
      		// Set the HTTP Response Content Length
      		response.setContentLength((int) pdfFile.length());
                  
      		// Set the HTTP Response Content Disposition based on the QueryString 'type' value
      		if ("save".equalsIgnoreCase(type)) {
      			response.addHeader("Content-Disposition", "attachment; filename=" + downloadFileName);
      		} else {
      			response.addHeader("Content-Disposition", "inline; filename=" + downloadFileName);
      		}
                  
      		// Create a FileInputStream for the PDF file
      		inp = new FileInputStream(pdfFile);
                  
      		// Create a BufferedInputStream based on the FileInputStream instance
      		buf = new BufferedInputStream(inp);
      
      		// Get an instance of the output stream
      		out = response.getOutputStream();
      
      		// Write the file content to the output stream
      		while ((readBytes = buf.read()) != -1) {
      			out.write(readBytes);
      		}
      
      	} catch (Exception e) {
      		// Log the exception in LiquidOffice
      		log.error(e);
      
      	} finally {
      		if (out != null) out.close();
      		if (inp != null) inp.close();
      		if (buf != null) buf.close();
      	}
      }
      

      How to setup the sample code
      The sample code which can be downloaded from here contains 2 form templates and a Java package. The forms need to be deployed in a folder called Forms, otherwise the form server side scripts needs to be modified to reflect the new path. The Java package once compiled needs to be copied to the LiquidOffice SharedResources Process directory under WEB-INF\lib.

      I hope that you find this article useful. Please feel free to leave me comment with queries/feedback regarding this topic or suggestions for future articles.





PRESTO – The data exchange protocol developed by the Government of France

4 01 2010

A couple of weeks ago I attended the launch of PRESTO – PRotocole d’Echanges Standard et Ouvert (which in English translates to Open and Standard Exchange Protocol). The DGME (the Government organisation responsible for the modernisation of the state) developed PRESTO in order to regulate the exchange of information between the different Government entities. PRESTO, which is now in its second iteration, defines a shared reliable and secure exchange profile in order to guarantee:

  1. Normalization of exchange
  2. Interoperability
  3. A sustainable and scalable implementation

PRESTO inter-organisation exchange protocol

The fact that the PRESTO specification is built on top of open standards such as SOAP, WSDL, WS-Addressing, WS-ReliableMessaging and XOP/MTOM does not come as a surprise. I will not dare to give my own explanation why Open Standards is so important to achieve interoperability at the technical level. Even though I did not have enough time to go through the specification in detail I am sure that the people at DGME have done a great job. Keep it up :)

A number of international software vendors / system integrators (namely Axway, Petals Link, IBM, Microsoft, Oracle and Tibco) collaborated closely with DGME and demonstrated how their technology can make use of the PRESTO specification. I was impressed by how much resources these companies were ready to invest in order to create these demos and other technology specific toolkits (which are being made freely available). We are not used to have such companies offer to collaborate/participate (for free) in Government of Malta initiatives. Anyone interested?





How does Unified Communications relate to eGovernment?

23 12 2009

Unified Communications (UC) is one of the must do according a number of leading analysts and IT vendors, mainly marketed as a way to decrease costs. During my research on this subject I have noticed the lack of articles suggesting how UC can lend be used to enable a new breed of eGov 2.0 services.

The general accepted definition of UC is ‘the integration of real-time communication services such as instant messaging (chat), presence information, IP telephony, video conferencing, call control and speech recognition with non real-time communication services such as unified messaging (integrated voicemail, e-mail, SMS and fax)’ 1. Instant messaging (IM) within an enterprise may be perceived as a way of allowing employees, irrespective of their geographic location, to exchange messages between each other securely. This is absolutely true, however it should not be considered as the only service that can be rendered. IM opens up a new opportunity to create a new communication channel with citizens. Federation with public IM providers such as GTalk, MSN Messenger and Yahoo Messenger enable a new breed of eGovernment services that will allow citizens to contact Government through mediums they are familiar with. Offhand I can think of three types of services that can be offered through IM:

1. Human interactive services

An example of such service would be a call centre service that would necessitate a number of Government employee (or a sub contractor) to respond to the general public questions

2. Automated services

These could range from simple request for public information systems (such as weather, government gazette headlines, latest news) to department specific systems that, given the proper authentication/authorization and security is in place, would provide the citizen with private information (such as social service contributions, tax returns, medical appointments).

3. Hybrid services

Hybrid services would be automated services that would escalate for human intervention in case that the citizen would require something that cannot be provided automatically.

Voice is another untapped eGovernment communication channel and in my opinion has the potential to engage elder or technophobic citizens that do not own and are not planning to buy a computer. Interactive Voice Response (IVR) systems have been used successfully for a number of years in banks and call centres. The development of IVR assisted systems has been simplified considerably and generally it is integrated in the main stream development integrated environments (IDE) and the technology has also been enriched with voice recognition. Can you imagine how many existent services can also be enabled with an IVR interface and the number of new services that can be created?

1 Wikipedia, 2009








Follow

Get every new post delivered to your Inbox.