Pages

Tuesday, January 3, 2017

Documentum D2 custom action plugins

The administrators of D2 can create custom menu items invoking your custom service classes. Menu items are created and set up in D2 config.

Invoking a native D2 service
But first, let's consider a native D2 service can be invoked. Suppose we want to display the value of some attribute of the selected object. For this getProperties methods of D2 PropertyService can be employed. The target property has to be specified in the message field. The returned value can be passed to some javascript function, for example, to be displayed in javascript alert popup.
Invoking a custom service class

Custom service methods must implement marking interface IPluginAction and have the common signature:

public class ActionServiceTemplate implements IPluginAction {

    public List<Attribute> someMethods(D2fsContext d2context) throws Exception {
        ParameterParser d2parameterparser = d2context.getParameterParser();

        // all the parameters received by method
        for (Attribute a : d2parameterparser.getParameters()) {
            System.out.println("paramName/value: " + a.getName() + " " + a.getValue());
        }

        // shortcut method to access the selected object id
        IDfId objectId = d2context.getFirstId();

        // ParameterParser method to retrieve values of received attributes
        String contentType = d2parameterparser.getStringParameter("aContentType");
        String containingFolderObjectId =d2parameterparser.getStringParameter("parentId");

        // the method returns list containing arbitrary nubmer of key value pairs
        List<Attribute> result = new ArrayList<>();
        result.add(AttributeUtils.createAttribute("result", "test"));
        return result;
    }
}

Depending on the menu item clicked, D2 user interface sends to the method various named values ( e.g. ContentType, parentId). The selected object id is always included. The method has only one argument of type D2fsContext. This type includes ParameterParser that is a container for the list of name value pairs each enclosed in Attribute class. An instance of Attribute holds name and value. The values can be directly accessed using ParameterParser method getStringParameter. Some additional examples of how some received values can be used I included the article of listener plugins.

D2fsContext also contains shortcut methods used to directly access some parameters, for example, getFirstId to get id of the selected objects. The selection might include single or multiple objects.

As it was demonstrated above, the method might optionally return a list of results in the same form i.e as Attributes. In D2 user interface the returned results will be available as javascript variables having the same names, for example above we used alert(object_name), that can be further processed by javascript.

The example below shows how to create a custom "Copy link to clipboard" action. The standard "Copy link to clipboard" menu item publishes D2_ACTION_COPY_LINK_IN_CLIPBOARD event that is processed by some Clipbard service that eventually puts the url to the selected object into clipboard.

If custom urls are needed, for example with different hostname and some object-dependent parameters, the custom action plugin is the solution.

public class CopyLink implements IPluginAction {

    public List<Attribute> copyLink(D2fsContext d2context) throws DfException, D2fsException, IOException {
        IDfId objectId = d2context.getFirstId();
        
        ParameterParser d2parameterparser = d2context.getParameterParser();
        String contentType = d2parameterparser.getStringParameter("aContentType");
        String url = "https://www.instagram.com/get?id=" + objectId.getId() + "&type=" + contentType;

        List<Attribute> result = new ArrayList<>();
        result.add(AttributeUtils.createAttribute("result", url));
        return result;
    }
}

The setting in D2 config should be adjusted as follows:

The plugin returns the custom url as the javascript variable named result. The result is passed to a native D2 javascript method pasteInClipboard that in turn calls the applet to put the value into the clipboard.

Action plugin executing javascript before executing a D2 event

Unfortunately D2 config does not allow executing javascript and then publishing D2 event to ajaxHub. After a service is executed, either javscript is executed (when JS in selected in Type list) or event is published (when EVENT is selected in Type list). When NATIVE is selected, the service results are ignored.

Suppose a confirmation pop up dialog is should appear when a user clicks on some standard action publishing event, for example when "Cancel checkout" is clicked. If the user clicks yes in the popup then the event is published. Alternatively, nothing happens, if no is selected.

I propose a working workaround allowing execute a javascript code before sending D2 event. When a user triggers action normally publishing an event, a custom service is invoked instead. The service does nothing but only returns javascript that executes arbitrary code, such as displaying the stand confirm popup, and then publishes an arbitrary event directly to openAjaxHub.

public class Relay implements IPluginAction {

    static String EVENT_NAME = "myActionName";
    static String CONFIRMATION_MESSAGE = "myActionMessage";

    public List<Attribute> relay(D2fsContext d2context) throws DfException, D2fsException, IOException {
        Map<String, String> parametersMap = new HashMap<>();
        ParameterParser pp = d2context.getParameterParser();

        // forward the original openAjax message together with the event to be published
        for (Attribute a : pp.getParameters()) {
            parametersMap.put(a.getName(), a.getValue());
        }
        Parameters parameters = new Parameters(parametersMap);

        String confimationMessage = pp.getStringParameter(CONFIRMATION_MESSAGE);
        String eventName = pp.getStringParameter(EVENT_NAME);

        // return a string with immediately-invoked javascript function expression
        String js = "(function(){if(confirm('" + confimationMessage + "')){var myPluginContainer=new OpenAjax.hub.InlineContainer(managedHub,'myPluginContainer',{Container:{onSecurityAlert:function(){},onConnect:function(){},onDisconnect:function(){}}}); var myPluginContainerClient=new OpenAjax.hub.InlineHubClient({HubClient:{onSecurityAlert:function(){}},InlineHubClient:{container:myPluginContainer}});myPluginContainerClient.connect(function(hubClient,success){if(success){hubClient.publish('" + eventName + "','" + parameters.toString() + "');console.log('connected and sent');managedHub.removeContainer(myPluginContainer);}else{console.log('failed to connect');}});}})()";

        List<Attribute> result = new ArrayList<>();
        result.add(AttributeUtils.createAttribute("result", js));
        return result;
    }
}

The plugin needs input parameters: the message for the confirm dialog and the event to publish. The script returned by the service is executed by eval function. Now when a user click "Cancel checkout" he will have to additionally press OK in the confirm popup. If the user clicks Cancel nothing will happen.

Action plugin handling multiple selected objects

Action plugins can handle multiple selections. The class template below demonstrate how one could access all the selected objects using a shortcut method getObject. The class additionally demonstrates that plugin does not need to return any results. To set up a plugin that do not return any values, the option NATIVE could be selected in the D2 config field Type.

public class WorkflowActions implements IPluginAction {

    public List<Attribute> startWorkflows(D2fsContext d2context) throws DfException, D2fsException {

        // Loop through all the selected documents
        int i = d2context.getObjectCount();

        for (int j = 0; j < i; j++) {
            IDfSysObject obj = (IDfSysObject) d2context.getObject(j);
            // do something to the object, for example start some workflow
            startWorkflow(obj);
        }

        return new ArrayList<>();
    }
    // very oversimplified method starting workflows on the input object
    void startWorkflow(IDfSysObject obj) throws DfException {
        String workflowName = obj.getTypeName() + " Workflow";
        D2SdkWorkflowLauncher workflowLauncher = new D2SdkWorkflowLauncher(obj.getSession(), workflowName);
        IDfWorkflow workflow = workflowLauncher.startWorkflow(obj, workflowName);
        workflow.setSupervisorName(obj.getSession().getLoginUserName());
    }
}

14 comments:

  1. I have tried with custom url using javascript and it works fine in IE but not in Chrome. In chrome I could see pasteInClipboard javascript function not working. Any idea on how to solve this?

    ReplyDelete
    Replies
    1. The answer is straightforward - chrome does not support applets. All D2 users use only IE, which is the only browser supporting java applets.

      Delete
    2. Hi, Thanks for your reply..
      But if you see pasteInClipboard function. it supports IE (applets) and other browsers (different for Safari). Also OOTB copy to clipboard is working fine in chrome with Java and CTF browser plugin. I am checking why this in not working with chrome if OOTB is working. Any help is appreciated.

      Delete
    3. pasteInClipboard merely calls an applet method that accesses clipboard. in fact first I found the method in the applet and then I found the javascript function that calls it.
      neither d2 nor me know how to manipulate clipboard with javascript

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Is it possible to add parameters in D2 menu for action plugins ? They will be retrieved with d2context.getParameterParser()

    ReplyDelete
    Replies
    1. Yes, for example see above section "Action plugin executing javascript before executing a D2 event". The described plugin uses two parameters: "myActionName" and "myActionMessage"

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi, we are facing issue in loading custom plugin in D2 after 4.7 upgrade. Any idea what can be the issue??
    When I restart my app and check the plugin info in About D2 option, it does not load my custom plugin. However, my plugin jar is already in place /WEB-INF/lib/custom_plugin.jar.

    Can you please help where I can look for this issue??
    I am new to D2 thus any info on this would be helpful.

    Thank you.

    ReplyDelete
  6. Thanks For sharing the Information The Information Shared Is Very valuable please Keep Updating Us The InFormation Shared Is Very Valuable Python Online Training Hadoop Online Training <a href="https://nareshit.com/data-science-online-training/>DataScience Online Training</a>

    ReplyDelete
  7. What is the jar file used for D2SdkWorkflowLauncher class?

    ReplyDelete
  8. Where should the custom serviced method be placed to it can be used in the config?

    ReplyDelete