Posts in Category "LiveCycle"

Populate a Drop Down List in LiveCycle Designer with Dynamically Generated XML

One way to populate a Drop-down List on a LiveCycle form is using XML binding. In the situation explained below, the XML document is dynamically generated and bound to the form’s data model within a LiveCycle process by querying the database.

Situation:

Querying a database for all the supervisors and their ID. Then populate a drop-down list on the form with a single Data Connection.

4 Steps:

  1. Create a DataConnection on your form with an XSD that includes unbound occurrence of the list items.
  2. Add a Drop-down list to the form
  3. Bind the data to the drop-down list
  4. Create an Action Profile process to pre-populate the form with data from the database

1. Create a DataConnection on your form with an XSD that includes unbound occurrence of the list items.

Since the drop down list is only one field in the form and we are using a single data connection to bind data to the form, insert the following element node for the Supervisor Drop-down List. The selected fields are to be used to hold the currently selected value in the drop-down list.

<xs:element name="supervisorList">
      <xs:complexType>
              <xs:sequence>
                   <xs:element name="supervisor">
                         <xs:complexType>
                             <xs:sequence maxOccurs="unbounded">
                                 <xs:element name="supervisorID" type="xs:int"/>
                                 <xs:element name="supervisorName" type="xs:string"/>
                             </xs:sequence>
                         <xs:complexType>
                     </xs:element>
              </xs:sequence>
       </xs:complexType>
 </xs:element>
<xs:element name="selectedSupervisorName" type="xs:string"/>
<xs:element name="selectedSupervisorID" type="xs:int"/>

To add the Data Connection to your form:

  • Right click on the Data View Tab
  • Select New Data Connection
  • Select XML Schema
  • Browse to the XSD in your Application
  • Select the root node and Press OK

2. Add a Drop-down list to the form

From the Object Library in LiveCycle Designer, drag the Drop-down List object on to the form.

3. Bind the data to the drop-down list

Under the Object Panel, click on the Binding Tab. Bind the item to the selectSupervisorID in the Data Connection.

After adding data binding, the Specify Item Values text should become a green hyperlink. If link text is not green, ensure that dynamic properties is turned on. Click on the Specify Items Value.

Fill out the fields as shown above. Notice the [x] in the Items field. This enables all the elements to be loaded in to the list.

4. Create an Action Profile process to pre-populate the form with data from the database

In order to pre-populate the Drop-down list before the user sees the form:

  1. Create an Action Profile for the form that will call a Prepare Data Process
  2. Inside the Prepare Data Process, Create an XML variable called SupervisorList
  3. Inside the Prepare Data Process, Query the Database and Build an XML Variable using the Service Query for Multiple Rows as XML Service (Foundation/JdbcService).  After applying a SQL Statement to retreive the Supervisor ID and Supervisor Name from the database, click on the elipses button next to XML Information and fill it according to the image below according to your XSD.
  4. Set the Output for Step 3 Service to be the XML Variable created in Step 2.
  5. Bind the XML to the XSD element used in the forms DataConnection using setValue

Now, when the user pulls up the form in Workspace, it comes pre-populated with all of the Supervisors Names. The value for each field will be the Supervisor ID in this case.

Compiling a PDF Portfolio in LiveCycle

Situation:

How to merge multiple PDFs (retreived from a Database BLOB) and compile them using DDX and LiveCycle (ADEP). The portfolio will build based on the number of documents stored inside the database.

In this situation we will be compiling a Cover Page, Content Document(s), and a Final Document. Depending on the workflow state, there could be 0-5 documents in the database under the Content Document(s) section. The process is flexible enough to handle this.

Create the DDX File:

  1. Start by looking at the Assembly Descriptor documentation and DDX Reference Guide.
  2. In Workbench: Create a New Application (File>New>Application)
  3. Inside the Application directory, create a new folder and name it Assets.
  4. Locate the Acrobat .nav File you want to use. It will be located on your filesystem here: [Acrobat Location]\Acrobat\Navigators. For this situation AdobeRevolve.nav is being used. Import the .nav file into your Application’s Assets folder.
  5. Create a DDX file
    1. Right Click on Asset Folder> New > Assembly Descriptor.
    2. Provide a name for the DDX document.
    3. Click Finish.
    4. The Document Builder Window should come up inside Workbench
  6. The DDX File in the End will look like this:
    1. Start by Selecting New Result>PDF and Name it CompiledPDF
    2. Drag the Portfolio Component Under the CompiledPDF
    3. Under the Portfolio Editor paste the following elements:
      <Portfolio>
        <ColorScheme scheme="darkblueScheme"/>
        <Header/>
        <WelcomePage/>
        <Schema/>
        <DisplayOrder/>
        <SortOrder/>
        <Navigator source="AdobeRevolve.nav"/>
      </Portfolio>
    4. Press Apply in the Editor
    5. Drag the Navigator icon under the Portfolio Icon
    6. Paste the following under the Navigator Editor:  <Navigator source=”AdobeRevolve.nav”/>
    7. Press Apply in the Editor
    8. Drag the Packaged Files Icon underneath the Portfolio Icon
    9. Then add Seven PDF Documents sources under the Packaged Files and Name them in their Source field accordingly
    10. For Only the CoverPage document (the first one), go under the Basic tab and select both “This is the Base Document” and “There must be at least one valid source in this document” and ensure both of these boxes are deselected for the other PDF Document.
  7. Validate the DDX and Save it

Create a Process that will retrieve the documents from the database BLOB, assign them to a document map, invoke the DDX, Output a single PDF Portfolio document

The process will be built to look something like this:

In this process you will need the following variables:

    • thePortfolio–document–Output–Required
    • retrievedDocument –document
    • mapPortfolioDocs–map <document>
    • totalContentDocs–int
    • counter–int
    • tempContentDocID– string
    • PortfolioAssemblerResult–AssemblerResult
Retrieve a specific PDF document from the Oracle database BLOB and store it in the local variable retrievedDocument by executing the following executeScript service.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import javax.naming.InitialContext;
 try {
   InitialContext context = new InitialContext();
   String queryQuery = [input your query here to get blob]
   //If you need to pass a process variable use "patExecContext.getProcessDataValue("/process_data/@counter");
   try {
    DataSource ds = (DataSource) context.lookup("java:/DDXDocs_DS");
    connection = ds.getConnection();
    queryStatement = connection.prepareStatement(queryQuery);
    results = queryStatement.executeQuery();
    if (results.next()) {
        java.sql.Blob documentBlob = results.getBlob(1);
        com.adobe.idp.Document document = new com.adobe.idp.Document(documentBlob.getBinaryStream());|
        patExecContext.setProcessDataValue("/process_data/@retrievedDocument", document);
     }
   }catch(Exception ex){
	System.out.println(ex.printStackTrace());
} finally {
    if (results != null) {
    	 results.close();
    }
    if (queryStatement != null) {
        queryStatement.close();
    }
    if (connection != null) {
        connection.close();
    }
   }
 } catch (Exception e) {
   e.printStackTrace();
 }

 

To test if a BLOB had been retrieved use the following route condition:

getDocLength(/process_data/@retrievedDocument)==0

 

If the document  was successfully retrieved, map it to the map <document> variable (mapPortfolioDocs) based on the id you gave it in the DDX source, using the setValue mapping below:

After retrieving all of the documents available, it is time to InvokeDDX. Using the Invoke DDX Service:
Finally, using the setValue operation, map the AssemblerResult to the output document variable (thePortfolio):

 

Configuring Watched Folder for PDF Generator

To configure a watched folder in the Admin Console for PDF Generator:

  1. Create a folder to be your watched folder on your filesystem
  2. Login to the ADEP Admin Console (http://[servername]:[portnumber]/adminui/)
  3. Navigate to Services > PDF Generator
  4. Click On Sources
  5. Category=PDF Generator, Click Filter
  6. Click on ‘GeneratePDFService: 1.0 ‘ [Documentation on GeneratePDFService]
  7. In the ‘End Points’ tab, choose ‘WatchedFolder’ from the dropdownlist
  8. Click ‘Add’ (Resource: Description of Watched Folder End Point properties)
  9. For ‘Description’, type something to the effect of “Watched Folder Endpoint PDFG”
  10. In ‘Enter path for the watched folder’, type in the fully qualified path to the folder you created in Step 1
  11. In the Operation Field, select CreatePDF2 from dropdown
  12. In the ‘Input Parameter Mappings’ section, for the ‘inputDocument’ field, select variable, type *.* (this will allow all supported input type files)
  13. In the ‘Output Parameter Mappings’ section, for the ‘Output PDF Document’ field, type %F%E.pdf (this will inherit the input filename and extension and append the .pdf extension.
  14. Click the Add button to save the changes.
  15. Navigate to the Watched Folder you created in Step 1, you should now see new directories: /input, /result, /staged, /failed, /preserve
  16. Test by copying a Word document to the /input folder. It should disappear a few seconds later
  17. If successful, the generated PDF will appear in /result directory. If it failed, the document and reason for failure appear in the /failed directory

Making LiveCycle Workspace Tasks Only Visible to Specific Users/Groups

If you want to make a Workspace task only visible to specific groups, you have to specify who can Invoke the task through the AdminUI.

Prior to setting this up, be sure that you have already set up the groups you will need in order to lock down tasks to specific users and groups. Also be sure that all the users that need to invoke processes through workspace are given the Workspace User Role.

  1. Login into Admin Console http[s]://[server]:[port]/adminui
  2. Services>Applications and Services>Service Management
  3. Click the category dropdown, choose the category your process belongs to, and click filter.
  4. Locate the Process you wish to lock down and click the name of theProcess
  5. Click security tab
  6. Click Add Principal
  7. Choose User/Group in the “In” dropdown and click filter
  8. Click on User or Group you wish to add
  9. Check INVOKE_PERM
  10. Click Add
  11. Test by logging into Workspace as a user that should be able to invoke a task, task card should be visible.
  12. Then log into Workspace as a user that should not be able to invoke a task, the task card should not be visible.

Repeat steps 6-10 for each user/group
Repeat steps 4-10 for each process

Note: If a user has the Contentspace User or an Administrator role, they may be able invoke most tasks

Required Digital Signatures in LiveCycle form

Description:
Recently, I had a project in which a form was being routed to three people in a serial fashion. However, prior to submitting the form we wanted to test whether the current user had signed the form in his/her designated spot. In order for the digital signatures to remain valid we could not make adjustments to the XML Schema and then use the RenderForm activity in LiveCycle– signatures would have been broken. So we had to put all the code to handle this in the form.

Situation:
There are three roles, Employee, Supervisor, and Reviewer. The form is routed through a LiveCycle process in that order. The Supervisor and Reviewer can either Approve or Decline the form. If the form is Declined it gets routed back to the Employee.

Setting up the form:

  • Add a Text Field, name it requiredSignature, and set its presence to Invisible and set its value to “Employee”
  • Add 3 digital signature fields and named them: employeeSignature, supervisorSignature, and reviewerSignature
  • Put the Approve/Decline option as a drop-down list into the form instead of in Workspace User Action so we did not have to depend on the AWS_ACTION drop down and Form Bridge because we are submitting as a PDF due to the digital signatures, everything is self-contained in the form.
  • The bottom of the form looks like this:

    Three Digital Signatures

    Three Digital Signatures

 

Code:

Three things you should know about the following code:

  1. signatureValidate returns:
    -1 — Not a signature field
    0 — Signature is blank
    1 — Unknown status
    2 — Signature is invalid
    3 — Signature of document is valid, identity of signer could not be verified
    4 — Signature of document is valid and identity of signer is valid.
  2. event.target.getField() you must include [0] in order target the designated field.
  3. [signatureField].mandatory makes that signatureField required prior to submit; [signatureField].disabled makes that signatureField no longer required in order to submit the form.

To be placed on the in the preSubmit Event for the form:

if (requiredSignature.rawValue==”Employee”){

var oState = event.target.getField(“employeeSignature[0]“).signatureValidate();

if (oState > 3){

employeeSignature.mandatory = “disabled”;
requiredSignature.rawValue=”Supervisor”;

}
else{

employeeSignature.mandatory = “error”;

}

}

else if (requiredSignature.rawValue==”Supervisor”){

var oState = event.target.getField(“supervisorSignature[0]“).signatureValidate();

if (oState > 3){

supervisorSignature.mandatory = “disabled”;
requiredSignature.rawValue=”Reviewer”;

}
else{

supervisorSignature.mandatory = “error”;

}

}else if(requiredSignature.rawValue==”Reviewer”){

var oState = event.target.getField(“reviewerSignature[0]“).signatureValidate();
if (oState > 3){

reviewerSignature.mandatory = “disabled”;

}

else{

reviewerSignature.mandatory = “error”;

}

}

else if(requiredSignature.rawValue==”Decline”){

requiredSignature.rawValue=”Employee”;

}

 

On the Supervisor Approve/Decline Drop-down list add the following code to the exit Event:

var oState = event.target.getField(employeeSignature[0]“).signatureValidate();

if(this.rawValue == “Approve”){

if (requiredSignature.rawValue==”Supervisor”){
//do nothing


}

else if(requiredSignature.rawValue==”Decline”){

requiredSignature.rawValue=”Supervisor”;

if (oState > 3){

supervisorSignature.mandatory = “error”;

}

supervisorSignature.presence = “visible”;
supervisorSignatureDate.presence = “visible”;

}

}

else{

if (requiredSignature.rawValue==”Supervisor”){

if (oState > 3)
{

requiredSignature.rawValue=”Decline”;

}
supervisorSignature.mandatory = “disabled”;

}

supervisorSignature.presence = “hidden”;
supervisorSignatureDate.presence = “hidden”;

}

 

Result:

If an Employee attempts to Submit the form without signing the form, he/she will get the following Error Message:

Employee Signature Error Message

Employee Signature Error Message

Setting Up Windows Service Dependencies – Oracle XE Example

Did you ever need a Windows service to startup, but only after other services are running? The LiveCycle Turnkey installation creates a dependency so that MySQL starts before JBoss. What if your application requires another DB such as Oracle XE to be running first?

Unfortunately, Oracle XE does not install this dependency, so that if you setup the Oracle XE DB to automatically start (the default installation), everything will be running, but you can’t access the XE DB. This can happen when the DB starts up before the TNS Listener, so that the TNS Listener doesn’t realize there’s a database running.

This Knowledge Base Article from Microsoft http://support.microsoft.com/kb/193888 describes how to do setup these dependencies, but the text does not match up with what I see in Windows 2003 EE R2, so I have attached some screen shots.

  1. Start->Run->regedit
  2. Navigate to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\OracleServiceXE (swap OracleServiceXE for your service)
  3. Create a new Multi-String Value, by right-clicking on the background in the folder’s contents
    create_multi-string_value.png
  4. Change the name of the value to “DependOnService”
  5. Edit the value (right-click and select modify) and add the services that should be started before this one. You need to enter the service name exactly as it appears in the Windows Services application. For Oracle XE, it is the “OracleXETNSListener” service.
    edit_value.png
  6. Click OK
    change_multi-string_value_name.png
    View larger image.
  7. Verify that it was updated properly by checking the service properties
    service_properties.png

    service_dependencies.png

Working with the DataExtractionAndUpdateDatabase ES2 Sample Application Part 1

I had a customer contact me for help in getting this sample application to work.
http://help.adobe.com/en_US/livecycle/9.0/samples/ServiceUsageSampleFormsDataExtractionAndUpdateDatabase.html

It’s a nice sample application that shows you how to extract data from a form, transform the XML using XSLT and then save it to a MySQL database table.

The directions don’t tell you how to create the MySQL database and this can be a challenge. You can find documentation on MySQL 5.1 (the version installed as part of an ES2 turnkey installation) here. To save you the trouble, I’ve summarized the steps you need to follow to make the process work.

Note: This assumes you are using a LiveCycle ES2 JBoss turnkey installation on Windows.

Note: There seems to be a problem with the Watched Folder end point configuration and I could only get this process to work by using the invoke command from Workbench. I’ll post a fix to the WatchedFolder configuration in another blog entry.

  1. Open a command prompt and navigate to the MySQL installation. This is {LiveCycle ROOT}\mysql\bin. So, in a turnkey installation with the default folders, you would need to go to C:\Adobe\Adobe LiveCycle ES2\mysql\bin
  2. The turnkey installation creates this MySQL database account. user = adobe, password=password. Login with the adobe user using the mysql command line tool. The command is:
    mysql -u adobe -p
    Enter “password” when prompted for the password.
    login-resized.png
  3. Now you can create a test database by entering the command:
    CREATE DATABASE test;
  4. Switch to this database by entering the command:
    use test
    Note: you do not need a semicolon (;) for this command
  5. Now you can create the table by entering the command:
    CREATE TABLE tb_DataExtractionAndUpdateDB(data BLOB);
    Your command line prompt should look like the screen shot below.
    create_db_and_table-resized.png

    Please leave the mysql prompt open so we can verify the process updates the table later on.
  6. Now the database and table are created. You should verify the configuration of the data source (test-ds.xml). This was part of the collateral/resources folder that you downloaded earlier.
    You only need to change the user from root to adobe, if you’re using the default MySQL user and password. If you’ve chosen a different user/password, make sure you update it here. Since I’m using the default adobe user, my configuration looks like this.
    test-ds-resized.png
    This file should be copied to your deploy directory. If you used the default installation settings, this would be:
    C:\Adobe\Adobe LiveCycle ES2\jboss\server\lc_turnkey\deploy
    Note:If you changed anything in this file, then you will need to restart the LiveCycle server (just JBoss, not Windows) for the change to take affect.

  7. Open the DataExtractionAndUpdateDatabase process in LC Workbench.
    process-resized.png
  8. Double-click on the properties for the SQL activity which has the caption “Persist data to Database”.
    process_properties.png
  9. Click on the button with a caption of “…” next to the SQL Statement property.
    sql_default-resized.png
  10. Click on the test button. In the “Test Result” box you should see “Number of rows affected: 1″. Let’s check this in the database. Go back to the mysql command prompt and type in
    select * from tb_DataExtractionAndUpdateDB;

    you should see this result:
    check_results_1.png
  11. We’ve now proven we can insert a null BLOB. Let’s test that one with data will work too. Change the SQL to as shown in the screenshot below and then test it. Don’t save the changes!
    sql_test-resized.png
  12. Now let’s go back to the mysql command prompt and run the query again. You should see this:
    check_results_2.png
  13. Congratulations! You’ve now proven that the sample application works in your turnkey environment.

LC Designer: Single Web Service with Many Services

One side-effect of using Web Services in LC Designer is that you create a lot of data connections; in LC Designer and other IDEs consuming Web Services is not a big deal. For Form Desingers in particular, they seem to be consuming a lot more than the other development groups like: Java, AJAX, etc. And what amounts to is a tens of unique Web Services called within a single form. The problem is keeping up with all the WSDL and server changes. I’ve watched a developer change twenty forms because a single webservice was changed. I’ll show you a simple technique that can solve these problems.

What I do instead of creating many web services is that I create a single web service: invoke(Stirng in, String out)

This web service does nothing else but take a String as input parameter and write a String out as an output parameter. For the format of the String I use JSON. So the web service is more like: invoke(JSON in, JSON out).

Within the JSON in my object has the following:

jsonInputObject:{

       serviceID, //Says which service to invoke.

       messageObject:  {

              //Message specific stuff here

        }

}

I like JSON since its natively represented in JavaScript and I can represent what ever I want. Since most of the web service calls from LC Designer are stateless, I don’t worry about the overhead of using a stateful mechanism which adds bloat. 

Using a single web service that makes a call for you on the server side has the following advantages:

  • Don’t have to stub out a unique service call for every service
  • All services are available to you during design time.
  • Unit testing is easier because you can test your JavaScript on the form against a fixed JSON test object.
  • Adding an optional input parameter or adding a additional return parameters doesn’t require regeneration of your stubbed code.

So below is an example of what my LC Worflow process looks like:

 

ExampleService.png

In this implementation, there are only two services. One to retreive group information and other for office information. Instead of creating two web services, I just have one. For this project I’ll keep adding services to this process. I’ve had projects where are twenty service calls are represented with a single web service call.

 

LiveCycle Development Techniques

Whenever I start development on a LiveCycle project, which usually involves LC Forms, LC Reader Extensions, LC Output, and LC PDF Generator. I utilize Acrobat for most of my prototyping and debugging. I’ll list the frequently used techniques:

  1. LC Reader Extensions – For those beginning to build forms with LC Designer know their form will be Reader Extended before going into production. I’ve seen cases where they will modify a form, manually Reader Extend, and then test their change. To save time, get a copy of Acrobat installed on your machine and use that to test changes. The other way is just too
    masochistic.
  2. LC Forms – Most likely you will be exporting data from a PDF or importing data into a PDF. You need to test a form or a generated XML that binds to a form. A quick way is to open the pdf in Acrobat then to -
    • Export Data from a form: Acrobat menu -> Forms -> Manage Form Data -> Export Data
    • Import Data into a form:  Acrobat menu ->  Forms -> Manage Form Data -> Import Data
  3. LC PDF Generator – You run into a problem where LC PDF Generator gives you an error while converting a non-PDF file. Open the non-PDF file in its native application and print as a PDF. The most common error is permission not to export data or print.
  4. LC Output – Here I prefer to use a unit test case, in most cases flattening a PDF will work file in Acrobat but making an API call reveals more details.

Hope this helps.

Updating Hostname of WSDL Connections in XDP with LiveCycle Form Rendering Process

A lot of times form developers develop form designs locally on their machines or on development servers. When dealing with web services, they would use the local or development instances of the web services by creating data connections pointing to WSDL URLs on localhost or dev.server.com. This is perfectly fine until the forms need to be migrated to other systems such as system test, stating or productions. The form developers would need to revisit the forms and update the data connections to point to the appropriate URLs depending on which environment the forms are being migrated to.
This becomes a big time consuming issue on projects dealing with a vast amount of form designs. So recently what I have been recommending my customers is to use a form rendering process that updates the WSDL data connections in the form design automatically on the fly as the form is requested by a user.


How does this work? (Please also refer to the process map below.)

  • The process reads the requested form design resource from the LiveCycle repository.
  • It then copies the content of the form design as XML to process variable and counts the number of WSDL data connections in the form.
  • Then it loops through every WSDL connections and update the domain name including the port with a new domain name extracted from a process variable called targetURL. (This targetURL variable is passed in automatically when you set up your the Render Service setting in your form variable.)
  • It then updates the form design variable with the new XML that has new WSDL information.
  • Finally LiveCycle renders the form design into a PDF. 
wsdl-updating-form-rendering-process.png

Requirements

  • LiveCycle Forms ES server.
  • The form design has to be in an XDP format.
  • The XDP has to be in the LiveCycle repository (e.g. database, Documentum, FileNet, etc.).
  • All web services used in the XDP have to be on the same server as the LiveCycle instance.
  • The only difference between web services on various environments must be the domain name and port. (e.g. http://dev:8080/soap/services/Weather?wsdl and http://staging/soap/services/Weather?wsld – OK but http://dev:8080/soap/services/Weather?wsdl and http:/staging/soap/services/Weather2?wsdl – Not OK)
Download

Download wsdl-updating-form-rendering-process.xml
and import the XML into LiveCycle Workbench as a process.

Further thoughts
  • If you need to reader extend your forms, add a Reader Extensions action at the end of the process map.
  • If your form design is in PDF, you could modify the process map to read the PDF resource from the repository and convert it to XDP.
  • What if you only deploy PDFs to your production system and don’t want to render them only the fly? You could render XDPs to PDFs as a batch process by creating a watched folder endpoint that invokes this form rendering proess.