Code Clause for the Add Program Command

This clause defines the commands for the program. Below are examples of program code that could be written to provide various functionality.

Legal characters in XML are the tab, carriage return, line feed, and the legal graphic characters of Unicode, that is, #x9, #xA, #xD, and #x20 and above (HEX). Therefore, other characters, such as those created with the ESC key, should not be used for ANY field, including business and administrative object names, description fields, program object code, or page object content.

This page discusses:

Format Definition Example Program

To be used in a format definition, a program object definition must include these characteristics:

  • The needsbusinessobject clause must be true.
  • The code clause must contain the command needed to execute the program and the syntax for the command must be appropriate for the operating system.
  • The code clause should end with the $FILENAME macro so the program opens any file. Enclose the macro in quotes to ensure that files with spaces in their names are opened correctly.

To launch Microsoft Word 97 or 2000 on Windows and open a document file for viewing and editing, the following program code might be used in the external program named MSWORD:

${PROG} /w “${FILENAME}”

The ${PROG} macro returns the command needed to execute the program defined by the file association mechanism of windows. The /w is needed for Word 2000, but is ignored for other versions.

To open multiple PDF files you can create an external Program with the following code:

${PROG} /n ${FILENAME} 

The /n is needed for multiple files to be opened without errors.

For a simple generic format that uses file associations, do not define any programs for the edit, view and print clauses of the format. For a more complex and flexible generic Windows format that will open any file for view, edit, or print based on its file association, include a condition exception for Word. For example:

tcl;eval {... parsing code to take out quotes / args / etc. from the registry if [string match *winword* ${PROG}] {    exec [${PROG} /w “${FILENAME}”] } else {    exec [${PROG} “${FILENAME}”] }}

On a UNIX system, you might use the following for a text format:

edit textedit “${FILENAME}"

The quotes allow the file name to contain spaces.

Action Program Example

You could define a program to be used as an action on a State as follows:

** 
 ** Note that the macros (EVENT,OBJECTID) required by the program must be explicitly 
 ** specified when the program is configured on the policy.  The macros are passed in 
 ** the 'args' array when the trigger is run. 
 ** 
 ** This trigger will increment the 'docInt' attribute on an object when it is promoted
 ** from state Created to state Working, and will decrement the attribute when an 
 ** object is demoted from state Working to state Created.
 **
 ** To configure this as a promote and demote action on a policy:
 ** mod policy docPolicy state Created add trigger promote action 
 **            docPromoteAction input "-method mxMain ${EVENT} ${OBJECTID}";
 ** mod policy docPolicy state Working add trigger demote action 
 **            docPromoteAction input '-method mxMain ${EVENT} ${OBJECTID}" ;
/** docPromoteAction: example java trigger program 
 *
/import matrix.db.*;
import matrix.util.*;

public class ${CLASSNAME}
{
    public ${CLASSNAME}(Context ctx,String[] args)
    {
    }
    public int mxMain(Context ctx,String[] args)
    {
        try
        {
            // Declarations
            String event = new String();
            String objId = new String();
            String attrName = new String("docInt");
            MQLCommand mql = new MQLCommand();
            // Get and check arguments arguments
            if (args.length > 0)
                event = args[0];
            if (args.length > 1)
                objId = args[1];
            // Generate notices for bad arguments
            if (event == null || event.equals("")) {
                mql.executeCommand(ctx, "notice 'No event for docPromoteAction'");
            }
            else if (objId == null || objId.equals("")) {
                mql.executeCommand(ctx, "notice 'No objId for docPromoteAction'");
            }
            // Make sure it is a promote/demote event
            if (!event.equalsIgnoreCase("promote") && !event.equalsIgnoreCase("demote'))
                return 0;
            // Construct and open the businessobject
            BusinessObject obj = new BusinessObject(objId);
            obj.open(ctx);
            // Get the current attribute value
            Attribute attrInt = obj.getAttributeValues(ctx, attrName);
            int val = Integer.parseInt(attrInt.getValue());
            if (event.equalsIgnoreCase("promote"))
                val++;
            else
                val--;
            // Update the attribute, and the business object
            attrInt.setValue(String.valueOf(val));
            AttributeList attrList = new AttributeList();
            attrList.addElement(attrInt);
            obj.setAttributes(ctx, attrList);
            obj.close(ctx);
        }
     catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return 0;
    }
}

It is recommended that actions and checks be configured as promote triggers, and not as lifecycle checks and actions.

Check Program Example

/*
 **
 ** docLockCheck: example java trigger program
 ** 
 ** Note that the macros (EVENT,OBJECTID) required by the program must be explicitly 
 ** specified when the program is configured on the policy. The macros are passed in 
 ** the 'args' array when the trigger is run.
 **
 ** This trigger reads the docString attribute on the object and compare it to the
 ** current user's name. It will block the event (return 1) if they do not match.
 **
 ** To configure this as a promote and demote action on a policy:
 ** mod type docType add trigger lock check 
 **          docLockCheck input "-method mxMain ${EVENT} ${OBJECTID}";
 **
 **
 */
import matrix.db.*;
import matrix.util.*;

public class ${CLASSNAME}
{


    public ${CLASSNAME}(Context ctx,String[] args)
    {
    }
    public int mxMain(Context ctx,String[] args)  throws Exception
    {

        // Initialize return value to "ok"
        int retval = 0;

        try
        {
            // Declarations
            String event = new String();
            String objId = new String();
            String attrName = new String("docString");
            MQLCommand mql = new MQLCommand();

            // Get and check arguments
            if (args.length > 0)
                event = args[0];
            if (args.length > 1)
                objId = args[1];
            // Generate notices for bad arguments
            if (event == null || event.equals("")) {
                mql.executeCommand(ctx, "notice 'No event for docLockCheck'");
            }
            else if (objId == null || objId.equals("")) {
                mql.executeCommand(ctx, "notice 'No objId for docLockCheck'");
            }

            // Make sure it is a lock event
            if (!event.equalsIgnoreCase("lock"))
                return 0;

            // Construct and open the businessobject
            BusinessObject obj = new BusinessObject(objId);
            obj.open(ctx);

            // Get the current attribute value and compare to current user
            Attribute attrString = obj.getAttributeValues(ctx, attrName);
            String attrUser = attrString.getValue();
            String currentUser = ctx.getUser();
            // If no match, block event
            if (!attrUser.equals(currentUser)) {
                retval = 1;
                String msg = "You are not user " + attrUser;
                System.out.println(msg);
                mql.executeCommand(ctx, "error '" + msg + "'");
            }
            // Close the object
            obj.close(ctx);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            retval = 1;
        }
        return retval;
    }
}

It is recommended that actions and checks be configured as promote triggers, and not as lifecycle checks and actions.

Example of Creating a Program for Execution As Needed

/*
 **
 ** docTableProgram: example java program for use in table 
 ** 
 ** This program returns the number of objects of type "docType" connected to the 
 ** current object by the relationship "docRel"
 **
 ** Note that the macro OBJECTID must be explicitly specified when the program is 
 ** configured in a table definition so it can be loaded into the 'args' array 
 ** program is executed.
 **
 ** To configure this as a table column:
 ** add table docTable column label Rels 
 **             businessobject program[docTableProgram -method mxMain ${OBJECTID}]
 **
 **
 */
import matrix.db.*;
import matrix.util.*;

public class ${CLASSNAME}
{

    public ${CLASSNAME}(Context ctx,String[] args)
    {
    }
    public String mxMain(Context ctx,String[] args)  throws Exception
    {

        // Initialize number of connected objects
        int retval = 0;

        try
        {
            // Declarations
            String objId = new String();
            MQLCommand mql = new MQLCommand();

            // Get and check arguments
            if (args.length > 0)
                objId = args[0];
            if (objId == null || objId.equals("")) {
                mql.executeCommand(ctx, "notice 'No objId for docTableProgram'");
            }

            // Construct and open the businessobject
            BusinessObject obj = new BusinessObject(objId);
            obj.open(ctx);

            // Get the connected objects
            String relType = "docRel";
            String objType = "docType";
            StringList objSelect = new StringList();
            StringList relSelect = new StringList();
            ExpansionWithSelect exp = obj.expandSelect(ctx,
                                                       relType,   // relationship pattern
                                                       objType,   // type pattern
                                                       objSelect, // selects on objectes
                                                       relSelect, // selects on rels
                                                       true,      // getTo direction
                                                       true,      // getFrom direction
                                                       (short)1);  // levels

            // And return the count of related objects
            RelationshipWithSelectList relList = exp.getRelationships();
            retval = relList.size();

            // Close the object
            obj.close(ctx);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return String.valueOf(retval);
    }
}