21 March 2016

1. File Manager

1.1. Triggers

The TVC Office Integration allows using custom triggers to perform actions upon certain events. There are several different events, which a trigger can act upon. Those events are: (please refer to the administration guide for more details about the event types).

  • On Download

  • After Download

  • On Upload

  • On Validate Upload

  • After Upload

A trigger is a Java class that is executed on the application server. The Java class must extend from com.technia.tvc.office.server.trigger.Trigger.

1.1.1. Registration of Triggers

All triggers must be registered in either the file WEB-INF/classes/TVCFileManager.xml or within a page object called TVC File Manager.

If you have a file called WEB-INF/classes/TVCFileManager.xml, this file will be read and the page object won’t be read.
Recommended is to always use the XML file approach.

Below is an example where three different triggers has been registered.

<FileManager>
    ...
    <Triggers>
        <Trigger name="Trigger 1" className="com.acme.tvc.office.triggers.Trigger1">
            <Matches fileName="*.xls"/>
            <Matches fileName="*.xlsx"/>
            <Matches fileName="*.ppt"/>
            <Matches fileName="*.pptx"/>
            <Matches fileName="*.doc"/>
            <Matches fileName="*.docx"/>
        </Trigger>

        <Trigger name="Trigger 2" className="com.acme.tvc.office.triggers.Trigger2">
            <Matches onlyIfCDM="true" format="format_generic">
                <Matches fileName="*.xls"/>
                <Matches fileName="*.ppt"/>
                <Matches fileName="*.doc"/>
            </Matches>
        </Trigger>

        <Trigger name="Trigger 3" className="com.acme.tvc.office.triggers.Trigger3">
            <Matches policy="policy_CADDrawing,policy_MyPolicy"
                     type="type_CADDrawing,type_MyType"
                     role="role_GlobalUser,role_CustomRole">
                <Matches fileName="*.xls"/>
                <Matches fileName="*.ppt"/>
                <Matches fileName="*.doc"/>
            </Matches>
        </Trigger>
    </Triggers>
</FileManager>

1.1.2. Trigger Example

Below is an example trigger that will set some properties. The uploaded properties are only printed out to the logger.

import com.technia.tvc.office.common.PropertyInfo;
import com.technia.tvc.office.server.trigger.AfterUploadInput;
import com.technia.tvc.office.server.trigger.OnDownloadInput;
import com.technia.tvc.office.server.trigger.Trigger;
import com.technia.tvc.office.server.trigger.TriggerAbort;
import com.technia.tvc.log4j.Logger;
import java.util.Date;
import java.util.Iterator;

public class MyTrigger extends Trigger {
    private static final Logger logger = Logger.getLogger(TestTrigger.class);

    @Override
    public void onDownload(OnDownloadInput ctx) throws TriggerAbort {
        ctx.addSetProperty(new PropertyInfo("property-1", true));
        ctx.addSetProperty(new PropertyInfo("property-2", new Date()));
        ctx.addSetProperty(new PropertyInfo("property-3", Math.PI));
        ctx.addSetProperty(new PropertyInfo("property-4", 123456789));
        ctx.addSetProperty(new PropertyInfo("property-5", "TVC property transfer test..."));
        ctx.addSetProperty(new PropertyInfo("property-6", false, true));
        ctx.addSetProperty(new PropertyInfo("property-7", new Date(0L), true));
        ctx.addSetProperty(new PropertyInfo("property-8", Math.E, true));
        ctx.addSetProperty(new PropertyInfo("property-9", 987654321, true));
        ctx.addSetProperty(new PropertyInfo("property-10", "lorem ipsum...", true));
    }

    @Override
    public void afterUpload(AfterUploadInput ctx) {
        Iterator<PropertyInfo> itr = ctx.getReturnProperties().iterator();
        while (itr.hasNext()) {
            PropertyInfo p = itr.next();
            logger.debug(">>>> Got property: '" + p.getName() + "' with value '" + p.getValue()
                    + "' (type=" + p.getType() + ")");
        }
    }
}

By default, properties are not sent back when the document is uploaded due to performance reasons. In many cases, the properties are only used down-stream, but to enable pushing the properties back to the server, you need to configure the File Manager to do so. You can do this in two different ways:

First alternative is to configure this by init-parameters within the TVC-servlet in WEB.XML:

<init-param>
    <param-name>tvc.office.client.propertyReadEnabled</param-name>
    <param-value>true</param-value>
</init-param>

The second alternative is to configure this within the WEB-INF/classes/TVCFileManager.xml file:

<FileManager>
    ...
    <Client propertyReadEnabled="true" .../>

1.2. Drop Zone

The TVC File Manager contains a feature that can be used within a TVC Structure Browser table page, allowing the user to drop files into ENOVIA. The files can be dropped onto existing Document objects, or any other kind of object. In the latter case, the drop zone contains configuration possibilities to define how to create the Document that will be created.

In some cases, there is a need to override the behavior of the drop zone, for example being able to have full control over how the Document is created and possible also create additional objects. To do so, you will need to implement a so called Drop Zone Handler (com.technia.tvc.office.ui.dropzone.table.DropZoneHandler).

Another situation were you need to implement your own Drop Zone Handler is when you need to have a pre-processing UI. Then your custom drop zone handler will define what page to load and how to load it.

Such a drop zone handler is defined on a column as shown below:

<Column>
    <ColumnType>dropzone</ColumnType>
    <Setting name="dropzone:type_Part" value="java:com.acme.MyDropZoneHandler"/>
</Column>

If you want to customize the drop zone used above the table, then you can do so by modifying the PageConfig and add a parameter as shown below:

<Parameters>
    <Parameter name="dropzone:handler" value="java:com.acme.MyDropZoneHandler"/>
</Parameters>

1.2.1. The Drop Zone Handler

The DropZoneHandler class has a number of methods that must be implemented and some that can be implemented.

Some notes:

  • Depending on if your drop zone handler works within a column or above the table, different methods are used.

  • The methods that are used regardless if the drop zone handler is registered for a column or above the table are listed below:

  • When your drop zone handler is implemented for a column, you have some additional methods that can should implemented to determine if the drop zone is available or not:

1.2.2. Drop Zone Pre Processing

If you have a need to show a user interface (pre processing) for the user after the files has been dropped, you will return such a UI from the getPreProcessUI() method.

An example of such is shown below:

public UserInterface getPreProcessUI(DropCtx ctx) throws TVCException {
    UserInterface ui = new UserInterface();
    ui.setHref(ActionUtils.getActionURL("/myDropPreProcess"));
    ui.setTarget(TargetLocation.SIDEPANEL);
    ui.setSidePanelWidth(400);
    return ui;
}

The Drop Zone framework will explicitly add information about what row and column the drop were made on. This information is required later on when you call the drop zone callback Java Script.

For simplicity, we have provided a base action class that you can extend from. This one is called com.technia.tvc.office.ui.dropzone.actions.DropZonePreprocess.

The pre process page will show some user interface, typically in the side panel, but could also be in a popup window. After the pre-process page is complete, you need to invoke a Java Script within the table frame with information about what files were dropped, what cell the drop took place upon and some additional parameters that you most likely want to access from your Drop Zone Handler.

var dataId = "<%= request.getParameter("dataId") %>";
var colIndex = <%= request.getParameter("col") %>;
var files = [];
<%
    String[] fileNames = request.getParameterValues("file");
    for (String fileName : fileNames) {
        out.write(String.format("files.push('%s');", fileName));
    }
%>
...
var args = {};
// collect the arguments in the args map...
var frame = parent.frames["tableContentFrame"].frames["tableBodyRight"];
frame.tvc.dropZone.afterPreprocess(dataId, colIndex, files, args);

Take a look into the file /tvc/office/dropzone/tvcSelectFormat.jsp that is part of the File Manager, which is a pre process page allowing the user to select file-format.

1.3. File Package Download

The file package download (FPD), is a feature of the File Manager component, which allows downloading multiple files in a so called package to a folder in the client’s computer and to automatically open a pre-defined file from the package.

The files within such a package can consist of dynamic data from the Matrix database, and/or checked in files.

The File Package Download PDF guide contains a lot of information how to define what to include in a package. You can by only making an XML configuration be able to solve many common use cases, however, there are possibilities to implement a Java handler that allows you to have even better control of the generated package.

1.3.1. FPD Registration

A FPD configuration is an XML file containing the FPD definition. There are some different ways how to define this file. The simplest way is to store one configuration in a separate file, and store it under

/WEB-INF/tvc/fpd/MyFPD.xml

Or, if you use name spaces for resources (which is preferred), then store it as the example below shows:

/WEB-INF/tvc/mynamespace/fpd/MyFPD.xml

1.3.2. FPD Handler

To write a custom FPD handler, you need to create a Java class that implements the interface called com.technia.tvc.office.server.fpd.Handler.

The only method, which your handler needs to implement, has the following signature:

package mypackage;

import com.technia.tvc.office.server.fpd.Handler;
import com.technia.tvc.office.server.fpd.HandlerCtx;

public class MyHandler implements Handler {
    public void perform(HandlerCtx ctx) throws TVCException {
        // logic goes here...
    }
}

Classes of interest are found from the API docs:

  • com.technia.tvc.office.server.fpd.Handler

  • com.technia.tvc.office,server.fpd.HandlerCtx

Within the perform method, you define what the package will contain. Everything added to the package is defined as being either a com.technia.tvc.office,server.fpd.HandlerCtx.PackageEntry or a com.technia.tvc.office,server.fpd.HandlerCtx.PackageDependency.

The difference between a PackageEntry and a PackageDependency is that while an Entry is a part of the Package, a Dependency is something that is downloaded after the package has been created but before the package is opened.

Currently the only dependency you can have is to a file checked-in to a business object in Matrix. The file might then be downloaded through the FCS server, if FCS is enabled in your environment. The benefit of not letting checked-in files being a part of the package, but being a dependency, is because of utilizing the benefits with the FCS architecture (i.e. reducing the need of transferring large amount of data over WAN).

Generally, one should reduce the size of the dynamic package if possible - because the package itself is always downloaded from the Main Collaboration Server (MCS), while dependencies can be downloaded from the File Collaboration Servers (FCS).

1.3.3. FPD Handler Example

Below is an example of a FPD handler. The code itself doesn’t perform any useful, just illustrating different ways to define the content of a package.

import com.technia.tvc.core.TVCException;
import com.technia.tvc.core.db.BusinessObjectUtils;
import com.technia.tvc.core.db.domain.DomainObject;
import com.technia.tvc.core.db.domain.to.FileTO;
import com.technia.tvc.core.html.io.XMLWriter;
import com.technia.tvc.core.util.XMLUtils;

import com.technia.tvc.office.server.fpd.Handler;
import com.technia.tvc.office.server.fpd.HandlerCtx;
import com.technia.tvc.office.server.fpd.HandlerCtx.PackageEntry;
import com.technia.tvc.office.server.fpd.HandlerCtx.TableExportEntry;
import com.technia.tvc.office.server.fpd.HandlerCtx.XMLWriterEntry;

import java.io.StringReader;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;

public class TestHandler implements Handler {

    public void perform(HandlerCtx ctx) throws TVCException {
        /*
         * Test MQL
         */
        String[] adminObjects = new String[]
            { "type", "attribute", "person", "role", "group", "page" };
        for (int i = 0; i < adminObjects.length; i++) {
            PackageEntry admin = ctx.addMQL("list " + adminObjects[i]);
            admin.setSaveAs(adminObjects[i] + "s.txt");
            admin.setSaveIn("admin");
        }

        /*
         * Test URL
         */
        PackageEntry url = ctx.addURL("/tvc/core/images/tvc_logo_big.gif");
        url.setSaveIn("images");
        url.setSaveAs("tvc_logo.gif");

        /*
         * Test adding input stream
         */
        PackageEntry is = ctx.add(getClass().getResourceAsStream("TestHandler.class"));
        is.setSaveIn("data");
        is.setSaveAs("TestHandler.class");

        /*
         * Test adding reader
         */
        PackageEntry reader = ctx.add(new StringReader("Hello world"));
        reader.setSaveIn("misc");
        reader.setSaveAs("helloworld1.txt");

        /*
         * Test adding plain string
         */
        PackageEntry data = ctx.add("Hello World!");
        data.setSaveIn("misc");
        data.setSaveAs("helloworld2.txt");

        /*
         * Test adding page-object content
         */
        PackageEntry page = ctx.addPageContent("TVC File Package Download Config");
        page.setSaveAs("TVCFilePackageDownloadConfig.xml");
        page.setSaveIn("admin");

        /*
         * Test XMLWriter
         */
        XMLWriterEntry xml = ctx.addXML();
        xml.setSaveIn("misc");
        xml.setSaveAs("xmldata.xml");
        XMLWriter xmlWriter = xml.getWriter();
        xmlWriter.startElement("hello");
        xmlWriter.startElement("world");
        xmlWriter.finish();

        /*
         * Test adding a DOM Document
         */
        try {
            Document document = XMLUtils.createBuilder(null).newDocument();
            document.appendChild(document.createElement("HelloWorld"));
            document.getDocumentElement().appendChild(document.createElement("Test"));

            PackageEntry doc = ctx.addXML(document);
            doc.setSaveIn("misc");
            doc.setSaveAs("doc.xml");
        } catch (ParserConfigurationException pce) {
            throw new TVCException(pce);
        }

        if (ctx.getUserData().getObjectIdCount() > 0) {
            String ids[] = ctx.getUserData().getObjectIds();
            for (int count = 0; count < ids.length; count++) {
                process(ctx, ids[count]);
            }
        }

        /*
         * Test adding a CSV export of a simple query
         */
        TableExportEntry tee = ctx.addTableData("Part", "*", "*", "*", "*", "", 100, true, null);
        tee.setCSVOutput();
        tee.setSaveIn("csv");
        tee.setSaveAs("Parts_FromQuery.csv");
    }

    private void process(HandlerCtx ctx, String objectId) throws TVCException {
        /*
         * Table Export
         */
        TableExportEntry tee = ctx.addExpandedData(objectId, "role_GlobalUser", new String[]
            { "EBOM From" }, 0, "TVC EBOM");
        tee.setXMLOutput();
        tee.setSaveAs("table_export.xml");
        tee.setSaveIn(objectId);

        /*
         * Matrix Export
         */
        String tnr[] = BusinessObjectUtils.getTNR(BusinessObjectUtils.newBusinessObject(objectId));
        PackageEntry export = ctx.addExportBus(
          tnr[0], tnr[1], tnr[2],
          false, false, false, true,
          true, true, true, false, null, null);
        export.setSaveIn(objectId);
        export.setSaveAs("export.xml");

        /*
         * Get related documents, if any, and do some additional testing.
         */
        DomainObject dom = DomainObject.newInstance(objectId);
        List<DomainObject> l = dom.getRelatedObjects("DOCUMENTS", "*", true, false);
        for (int i = 0; i < l.size(); i++) {
            DomainObject related = l.get(i);

            /*
             * Export again...
             */
            PackageEntry exportEntry = ctx.addExportBus(
                    related.getType(),
                    related.getName(),
                    related.getRevision());
            exportEntry.setSaveIn(objectId);
            exportEntry.setSaveAs(String.format("%s_%s.xml",
              related.getName(),
              related.getRevision()));

            /*
             * Test file checkout
             */
            FileTO files[] = related.getFiles();
            for (int j = 0; j < files.length; j++) {
                PackageEntry fileEntry = null;
                if ((j % 2) == 0) {
                    fileEntry = ctx.addFileDependency(
                      related.getObjectId(),
                      files[j].getFormat(),
                      files[j].getFileName());
                } else {
                    fileEntry = ctx.addFile(
                      related.getObjectId(),
                      files[j].getFormat(),
                      files[j].getFileName());
                }
                fileEntry.setSaveAs(files[j].getFileName());
                fileEntry.setSaveIn(objectId + "/files");
            }
        }
    }
}