21 March 2016

Table of Contents

© Copyright 2003-2019 by TECHNIA AB

All rights reserved.

PROPRIETARY RIGHTS NOTICE: This documentation is proprietary property of TECHNIA AB. In accordance with the terms and conditions of the Software License Agreement between the Customer and TECHNIA AB, the Customer is allowed to print as many copies as necessary of documentation copyrighted by TECHNIA relating to the software being used. This documentation shall be treated as confidential information and should be used only by employees or contractors with the Customer in accordance with the Agreement.

This product includes software developed by the Apache Software Foundation. (http://www.apache.org/).

2. Introduction

The TVC Collaboration feature adds a set of functionality that allows users to collaborate around ENOVIA™ data in various ways. Some of the features are:

  • Discussions in context of ENOVIA™ objects

  • Real time notifications on discussion events

  • Inbox to view received notifications

  • Profile page with settings and ability to upload an avatar

2.1. System Requirements

The following is a list of the requirements for the installation of this TVC. If your system is different, consult Technia before installing. For best performance it is recommended to use the latest version of browsers and application servers.

  • Requires ENOVIA™ V6R2013 or any later version.

  • Application Server according to the ENOVIA™ core product requirements that is also in the list of supported platforms for the Atmosphere framework (see disclaimer below).

    • If you are using multiple application servers, you must read the next chapter.

  • For the Exalead™ powered features you need to install and setup Exalead

Disclaimer: This feature uses a framework called Atmosphere for sending notifications from the server to the clients. This framework supports a wide range of browser and app servers. We will not be able to add support for browsers and/or app servers that is outside of this frameworks support. Also we might not be able to fix issues that could appear in the usage of this framework. See following pages for a list of this frameworks supported platforms and known issues:

Web browser IE11 in IE9 mode is not supported.

2.2. Multiple Application Servers

Many environments are scaled horizontally to support a larger amount of users. When having > 1 application server you need to use a load balancer that can divide the users across these application servers somehow.

The Collaboration component uses the web socket technology for setting up persistent connections between clients and servers and this technology has a completely different analogy than the traditional request/response paradigm used in HTTP/HTTPS communication. This will have some impact on your infrastructure since your load-balancer must be able to deal with web sockets correctly.

Technia cannot support every possible infrastructure combination. We can however share information how we have tested it, on what software’s and our configurations. To enable this feature in your own environment / setup-combination you must consult the documentation of your infrastructure components.

2.3. App-to-App-Server Communication

Since users in fact are connected to different application servers, but still should be able to receive notifications from other users – there is a need for the application servers to be able to speak with each other.

There are currently two different implementations available that solves this, one of the implementation uses HTTP communication between the servers and the second implementation uses the Open Source library named Ehcache. The latter is the default communication strategy.

The choice of strategy is defined with a TVC init-parameter. The name of the parameter and the possible values are shown below:

tvc.collaboration.appServerNotificationStrategy = ehcache | http

2.3.1. Ehcache™

The Ehcache™ component allows us to setup caches which are replicated among all the other application servers in the same cluster. This replication is done via the RMI protocol and each application server in the cluster is joining a TCP multicast-group.

The strategy of this implementation will try to automatically setup the network communication, but, there are some init-parameters that you may want to fine-tune for some reason or other.

For example, the port that we bind to is automatically picked by selecting a random free port within a specified range. Most likely, you want to define the port manually and in some cases also the network interface to bind to. If your host has multiple network interfaces you might need to specify the host name also. By default, the host name is resolved via the Java API:

InetAddress.getLocalHost().getHostAddress()

Defining the port and host can be done either with JVM system parameters OR TVC init parameters. The parameter name is the same regardless if you define them as a JVM system parameter or as a TVC init parameter. Below is an example how to define as JVM system parameter.

-Dtvc.collaboration.cache.listener.hostName=172.16.16.100
-Dtvc.collaboration.cache.listener.port=2010

The table below shows the additional TVC init parameters that you may want to change in some cases.

Init Parameter Name Description Default Value

tvc.collaboration.cache.listener.portRangeStart

If you have defined a port explicitly as described above, this parameter can be ignored.

Defines the lowest port number that we will try to use.

4001

tvc.collaboration.cache.listener.portRangeEnd

See above. Defines the highest port number that we might use.

41020

tvc.collaboration.cache.listener.socketTimeoutMillis

The number of milli-seconds client sockets will wait when sending messages to this listener until they give up.

2000

tvc.collaboration.cache.provider.multicastGroupAddress

The multi-cast address to be used.

230.0.0.1

tvc.collaboration.cache.provider.multicastGroupPort

The multi-cast port to be used.

446

tvc.collaboration.cache.provider.timeToLive

You can control how far the multicast packets propagate by this setting. Using the multicast IP protocol, the timeToLive value indicates the scope or range in which a packet may be forwarded.

By convention:

  • 0 is restricted to the same host

  • 1 is restricted to the same subnet

  • 32 is restricted to the same site

  • 64 is restricted to the same region

  • 128 is restricted to the same continent

  • 255 is unrestricted

1

tvc.collaboration.exalead.cache.replicateAsynchronously

whether replications are asynchronous (true) or synchronous (false)

false

2.3.2. HTTP Communication

When selecting the HTTP communication approach, each app-server must be able to connect to the other app server(s) in the cluster via HTTP. The implementation will try to identify the URL of the Application server automatically, but, this might in many cases fail due to the setup of load-balancers / proxies and hence you must inform each application server about their own HTTP address. This is done easiest by providing a JVM system parameter to the application server as shown below:

-Dtvc.collaboration.appServerURL=http://the-appserver-machine:8080/enovia

2.4. Test Scenario

Technia have during the development of the Collaboration component tested on a setup using an Apache HTTPD server acting as a load-balancer and behind having a couple of app servers running Apache Tomcat. All on a Windows 7 host.

The Apache HTTPD server has lately improved their support for web-socket tunneling, and we were using the version 2.4.10.VC11 (64 bit).

The Apache Tomcat versions were 7.0.56 (64 bit).

Below are the configurations we made. Note that we only provide these as examples and you should and might need to adjust them to your needs.

2.4.1. Apache HTTPD Configuration

The modules we explicitly enabled were:

  • proxy_module

  • proxy_ajp_module

  • proxy_balancer_module

  • proxy_wstunnel_module

  • lbmethod_byrequests_module

  • lbmethod_bytraffic_module

  • slotmem_shm_module

Proxy configuration

<Proxy balancer://ws>
BalancerMember ws://inv3373:8080 route=t8009
BalancerMember ws://inv3373:8081 route=t8010
ProxySet lbmethod=bytraffic
ProxySet stickysession=JSESSIONID
</Proxy>

ProxyPass /enovia/collaboration/communication balancer://ws/enovia/collaboration/communication

<Proxy balancer://ajp>
BalancerMember ajp://inv3373:8009 loadfactor=1 route=t8009
BalancerMember ajp://inv3373:8010 loadfactor=1 route=t8010
ProxySet lbmethod=bytraffic
ProxySet stickysession=JSESSIONID
</Proxy>

ProxyPass /enovia balancer://ajp/enovia

2.4.2. Tomcat Configuration

Each Tomcat instance were started up using a dedicated start script. Since they were all running on the same machine, we had to configure the ports per instance.

In that start script we configured a number of CATALINA_OPTS as shown below.

set CATALINA_OPTS=-Dtvc.collaboration.appServerURL=http://localhost:8080/enovia
set CATALINA_OPTS=%CATALINA_OPTS% -Dtomcat.server.port=8005
set CATALINA_OPTS=%CATALINA_OPTS% -Dtomcat.http.port=8080
set CATALINA_OPTS=%CATALINA_OPTS% -Dtomcat.redirect.port=8443
set CATALINA_OPTS=%CATALINA_OPTS% -Dtomcat.ajp.port=8009

The Tomcat server.xml file were changed (note for readability, only changed parts included):

<Server port="${tomcat.server.port}"

<Connector port="${tomcat.http.port}" redirectPort="${tomcat.redirect.port}" />
<Connector port="${tomcat.ajp.port}" protocol="AJP/1.3" redirectPort="${tomcat.redirect.port}" />

<Engine name="Catalina" defaultHost="localhost" jvmRoute="t${tomcat.ajp.port}">

2.4.3. Apache HTTPD Websocket Proxy Configuration

If apache is used to proxy the request to the tomcat server, websocket request could be dropped. Websocket communication is required for collaboration myspace to work properly.

Following is the configuration done in one of the httpd.conf used in apache with ENOVIA 2017x to proxy websocket requests.

3DSpace_httpd_fragment.conf

# to be added in <VirtualHost _default_:443> section of httpd-ssl.conf
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "444"

# TVC Collaboration Websocket configuration Starts

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

RewriteEngine on
ProxyRequests Off
ProxyPreserveHost on

ProxyPass /3dspace/collaboration/communication ws://localhost:8090/3dspace/collaboration/communication
ProxyPassReverse /3dspace/collaboration/communication ws://localhost:8090/3dspace/collaboration/communication

# TVC Collaboration Websocket configuration Ends

ProxyPass /3dspace http://localhost:8090/3dspace
ProxyPassReverse /3dspace http://localhost:8090/3dspace

In Enovia versions which are higher than 2017x and using location-based httpd configuration, proxy pass entries can be added in 3DSpace_httpd_fragment.conf in following way.

# TVC Collaboration Websocket configuration Starts

<Location /3dspace/collaboration/communication>
ProxyPass ws://localhost:9080/3dspace/collaboration/communication
ProxyPassReverse ws://localhost:9080/3dspace/collaboration/communication
</Location>

# TVC Collaboration Websocket configuration Ends

3. Installation

After installing TVC, you must perform three tasks in order to take this feature in to use.

3.1. Task 1 – Modifying emxNavigator.jsp

The first task is needed in order to include some functionality within the "emxNavigator.jsp" page (resides in the common folder in your application directory). The only change needed in this file, is to add a single line of code.

Just after the "body" element tag, add the line below (shown in bold):

<body ...>
<%@include file = "/tvc/collaboration/emxNavigator.jspf" %>
...

3.2. Task 2 – Modifying emxNavigatorDialog.jsp

The second task is needed in order to include some functionality within the "emxNavigatorDialog.jsp" page (resides in the common folder in your application directory). The only change needed in this file, is to add a single line of code.

Just after the "body" element tag, add the line below (shown in bold):

<body ...>
<%@include file = "/tvc/collaboration/emxNavigator.jspf" %>
...

3.3. Task 3 – web.xml

The third task needed is the registration of the Atmosphere servlet that is used for sending notifications from the server to the client (https://github.com/Atmosphere/atmosphere). The following lines should be added to web.xml

<servlet>
    <servlet-name>AtmosphereServlet</servlet-name>
    <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>org.atmosphere.cpr.packages</param-name>
        <param-value>com.technia.tvc.collaboration.core.io</param-value>
    </init-param>
    <init-param>
        <param-name>org.atmosphere.annotation.packages</param-name>
        <param-value>com.technia.tvc.collaboration.core.io</param-value>
    </init-param>
    <load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>AtmosphereServlet</servlet-name>
    <url-pattern>/collaboration/*</url-pattern>
</servlet-mapping>

3.4. TVC Stand-alone Pages

Collaboration panel are also rendered in stand-alone tvc pages like /launchFromPortal, /loadCategoryTopPanel, /loadTopPanel and /searchBasedTable based on configuration.

Following is the default configuration.

tvc.core.collaboration.inject.pages=launch-portal|top-panel|category-panel|search-table

If collaboration panel injection is not required on any of the supported pages, that page can be removed in the configuration.

tvc.core.collaboration.inject.pages=top-panel|category-panel

Even on the supported stand-alone pages, Collaboration panel is rendered by default only if the page is top most.(Popup or browser tab)

That behavior could be changed with the following property configuration.

tvc.core.collaboration.inject.top.only=true

This could be used in application where emxNagivator.jsp is not configured with collaboration and tvc pages only used for navigation.

image

Figure: Collaboration panel for /launchFromPortal

image

Figure: Collaboration panel for /loadTopPanel

image

Figure: Collaboration panel for /loadCategoryTopPanel

image

Figure: Collaboration panel for /searchBasedTable

3.5. Provider

There are three built-in providers available in Collaboration. They are "config"(default), "enovia" and "exalead". The provider value can also be a class full-name which extends "com.technia.tvc.collaboration.core.model.Provider". The provider can be configured as below.

tvc.collaboration.providerClass=config

The default "config" provider enables possibility to have Discussion and Workflow components run in different mode.

tvc.collaboration.discussion.mode=exalead
tvc.collaboration.workflow.mode=enovia

The other "enovia" or "exalead" provider makes both Discussion and Workflow run in same mode.

3.6. Exalead™

In order to use any of the features that requires Exalead you need to install Exalead and set it up for collaboration. To enable Exalead for Collaboration there is a TVC Init parameter that needs to be set.

tvc.collaboration.providerClass=exalead

or

tvc.collaboration.providerClass=config
tvc.collaboration.discussion.mode=exalead
tvc.collaboration.workflow.mode=exalead

In order for Exalead to know what data to index for Collaboration the following needs to be added to your Exalead index configuration file.

<BOTYPEFIELDS name="TVCCollaborationSet">
    <FIELD name="TVC_COLLAB_SET_TYPE" select="type" type="STRING" />
    <FIELD name="TVC_COLLAB_SET_ID"
        select="program[TVCCollaborationIndex -method getSetId ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_SET_OWNER" select="owner" type="STRING" />
    <FIELD name="TVC_COLLAB_SET_CONTEXT_ID"
        select="program[TVCCollaborationIndex -method getSetContextIds ${OBJECTID}]"
        type="STRING" />

    <FIELD name="TVC_COLLAB_NOTIFICATION_USER_NAME"
        select="program[TVCCollaborationIndex -method getSelectDataList ${OBJECTID} 'to[TVC Collaboration Notification].from.owner']"
        type="STRING" />
    <FIELD name="TVC_COLLAB_NOTIFICATION_USER_TAGS"
        select="program[TVCCollaborationIndex -method getNotificationUserTags ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_NOTIFICATION_CATEGORIES"
        select="program[TVCCollaborationIndex -method getNotificationCategories ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_NOTIFICATION_STATUS"
        select="program[TVCCollaborationIndex -method getNotificationStatus ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_NOTIFICATION_UNREAD_USERS"
        select="program[TVCCollaborationIndex -method getNotificationUnreadUsers ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_FOLLOWUP_USER_NAME"
        select="program[TVCCollaborationIndex -method getSelectDataList ${OBJECTID} 'to[TVC Collaboration Followup].from.owner']"
        type="STRING" />
    <FIELD name="NAME"
        select="program[TVCCollaborationIndex -method getSetSortValue ${OBJECTID}]"
        type="STRING" fastsort="true" />
</BOTYPEFIELDS>

<BOTYPE name="TVC Collaboration Thread" includes="TVCCollaborationSet,TVCCollaborationThread" />

<BOTYPEFIELDS name="TVCCollaborationThread">
    <FIELD name="TVC_COLLAB_THREAD_CLOUDS"
        select="program[TVCCollaborationIndex -method getClouds ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_FIRST_MSG_SENT_FROM" select="from[TVC Collaboration First Message].to.owner"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_FIRST_MSG_SENT_TO"
        select="program[TVCCollaborationIndex -method getSelectDataList ${OBJECTID} 'from[TVC Collaboration First Message].to.from[TVC Collaboration Sent To].to.owner']"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_HAS_ATTACHMENT"
        select="from[TVC Collaboration First Message].to.format.hasfile"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_IMPORTANT"
        select="from[TVC Collaboration First Message].to.attribute[TVC Collaboration Important]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_LAST_MSG_SENT_FROM" select="from[TVC Collaboration Last Message].to.owner"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_LAST_MSG_SENT_TO"
        select="program[TVCCollaborationIndex -method getLastMessageSentTo ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_LAST_MSG_SENT_CC"
        select="program[TVCCollaborationIndex -method getLastMessageSentCc ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_PRIVATE" select="attribute[TVC Collaboration Private]"
        type="BOOLEAN" />
    <FIELD name="TVC_COLLAB_THREAD_SENT_FROM" select="from[TVC Collaboration Thread Message].to.owner"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_SENT_TO"
        select="program[TVCCollaborationIndex -method getSelectDataList ${OBJECTID} 'from[TVC Collaboration Thread Message].to.from[TVC Collaboration Sent To].to.owner']"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_SUBJECT" select="attribute[TVC Collaboration Subject]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_SYSTEM_TAGS"
        select="program[TVCCollaborationIndex -method getSystemTags ${OBJECTID}]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_THREAD_USER_TAGS"
        select="program[TVCCollaborationIndex -method getUserTags ${OBJECTID}]"
        type="STRING" />
    <!-- Example of custom index field for a specific tag
    <FIELD name="CUSTOM_TAG_FIELD_OFFICE"
        select="program[TVCCollaborationIndex -method getIndexedSystemTag ${OBJECTID} 'office']"
        type="STRING" /> -->
</BOTYPEFIELDS>

<BOTYPE name="TVC Collaboration Workflow" includes="TVCCollaborationSet,TVCCollaborationWorkflow" />

<BOTYPEFIELDS name="TVCCollaborationWorkflow">
    <FIELD name="TVC_COLLAB_WORKFLOW_TITLE" select="attribute[TVC Collaboration Workflow Title]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_WORKFLOW_STATUS" select="attribute[TVC Collaboration Workflow Status]"
        type="STRING" />
    <FIELD name="TVC_COLLAB_WORKFLOW_COMPLETION_DATE"
        select="attribute[TVC Collaboration Workflow Completion Date]" type="STRING" />
    <FIELD name="TVC_COLLAB_WORKFLOW_DUE"
        select="program[TVCCollaborationIndex -method getWorkflowDue ${OBJECTID}]"
        type="BOOLEAN" />
    <FIELD name="TVC_COLLAB_TASK_ASSIGNED"
				select="program[TVCCollaborationIndex -method getTaskAssigned ${OBJECTID}]"
				type="STRING" />
    <FIELD name="TVC_COLLAB_TASK_STATUS"
				select="program[TVCCollaborationIndex -method getSelectDataList ${OBJECTID} 'from[TVC Collaboration Item].to[*|revision==last].attribute[TVC Collaboration Task Status]']"
				type="STRING" />
</BOTYPEFIELDS>

Sorting in Exalead is only possible to do on the field called "NAME". In case you have other information you need to index and sort on you will probably already have a name field in your config.xml. This means that objects indexed by TVC Collaboration needs to coexist with other objects on what data to index in the name field.

The way forward is to make a JPO in which you determine if the object belongs to TVC Collaboration or not. In case it is a TVC Collaboration object the value to index is determined by Collaboration otherwise it is delegated to the Exalead standard indexing mechanism.

Example code snippet handling the name field:

private static final Collection<String> TVC_COLLABORATION_TYPES = Arrays.asList("TVC Collaboration Thread", "TVC Collaboration Workflow");

if (TVC_COLLABORATION_TYPES.contains(type)) {
    IndexProgram.getMessageSortValueJPO(ctx, args);
    // Or if you have access to the object id directly
    //IndexProgram.getMessageSortValue(objectId);
} else {
    // Logical handling all other type of objects
}

To optimize the indexing performance when there is no other BOTTYPE configutation other than Collaboration specific configuration, use the below:

<INDEXER>
    <LINKTARGET active="false" size="100000"/>
    <!--
     .. other configurations
    -->
</INDEXER>

3.6.1. Exalead Index Configurations

Below properties can be configured to alter default behaviour.

Property Description Default Value

tvc.collaboration.exalead.index.partial

thread is pushed to partial indexing

true

tvc.collaboration.exalead.thread.modified

thread modified date is updated

true

tvc.collaboration.search.pagesize

Result count returned by exalead. This property will be used in case page size is not defined in Workflow find API execution

100

3.7. Elasticsearch™

In order to use any of the features that requires Elasticsearch you need to install Elasticsearch and set it up for collaboration. We have tested it for version 7.1.1 and it should work for later versions as well. It is only supported for discussions at the moment and not for workflows. To enable Elasticsearch for Collaboration there is a TVC Init parameter that needs to be set.

tvc.collaboration.providerClass=elastic

or

tvc.collaboration.providerClass=config
tvc.collaboration.discussion.mode=elastic
tvc.collaboration.workflow.mode=enovia|exalead

In order to control certain aspects of Elasticsearch, following configuration possibilities exists.

Property Key values Description

tvc.collaboration.elasticSearch.hosts

http://localhost:9200 (default)

specify the location of elasticsearch instance

tvc.collaboration.elasticSearch.number_of_shards

1 (default)

specify number of shards to be setup

tvc.collaboration.elasticSearch.number_of_replicas

1 (default)

specify number of replicas to be setup. This is for fail-over/high availability in case some node crashes

tvc.collaboration.elasticSearch.max_result_window

1000 (default)

specify max number of results to be returned when querying elasticsearch

Requires Java 1.8 or any later version.

3.7.1. Elasticsearch configurations

Move the required jar

Move the tvc-elastic-dependency.jar from ../<webapp-name>/WEB-INF/collaboration to ../<webapp-name>/WEB-INF/lib directory.

Configurations in TVCDiscussionConfig.xml

In order for Elasticsearch to know what data to index for Collaboration the following needs to be added to your TVCDiscussionConfig.xml file.

<ElasticSearch>
		<Endpoint>/tvc_collaboration_discussion</Endpoint>
		<Method>PUT</Method>
		<Schema><![CDATA[{
			"properties" : {
				"_mxid" : {
					"type" : "keyword",
					"expression" : "id"
				},
				"_physicalid" : {
					"type" : "keyword",
					"expression" : "physicalid"
				},
				"TVC_COLLAB_SET_NAME" : {
					"type" : "keyword",
					"expression" : "name"
				},
				"TVC_COLLAB_SET_TYPE" : {
					"type" : "keyword",
					"expression" : "type"
				},
				"TVC_COLLAB_SET_ID" : {
					"type" : "keyword",
					"expression" : "id"
				},
				"TVC_COLLAB_SET_OWNER" : {
					"type" : "keyword",
					"expression" : "owner"
				},
				"TVC_COLLAB_SET_CONTEXT_ID" : {
					"type" : "text",
					"expression" : "to[TVC Collaboration Discussion].from.to[TVC Collaboration Discussion Container].from.id"
				},
				"TVC_COLLAB_NOTIFICATION_USER_NAME" : {
					"type" : "text",
					"expression" : "to[TVC Collaboration Notification].from.owner"
				},
				"TVC_COLLAB_NOTIFICATION_USER_TAGS" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.NotificationUserTagsDataHandler"
				},
				"TVC_COLLAB_NOTIFICATION_CATEGORIES" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.NotificationCategoriesDataHandler"
				},
				"TVC_COLLAB_NOTIFICATION_STATUS" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.NotificationStatusDataHandler"
				},
				"TVC_COLLAB_NOTIFICATION_UNREAD_USERS" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.NotificationUnreadUsersDataHandler"
				},
				"TVC_COLLAB_FOLLOWUP_USER_NAME" : {
					"type" : "text",
					"expression" : "to[TVC Collaboration Followup].from.owner"
				},
				"TVC_COLLAB_THREAD_CLOUDS" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.CloudDataHandler"
				},
				"TVC_COLLAB_THREAD_FIRST_MSG_SENT_FROM" : {
					"type" : "keyword",
					"expression" : "from[TVC Collaboration First Message].to.owner"
				},
				"TVC_COLLAB_THREAD_FIRST_MSG_SENT_TO" : {
					"type" : "text",
					"expression" : "from[TVC Collaboration First Message].to.from[TVC Collaboration Sent To].to.owner"
				},
				"TVC_COLLAB_THREAD_HAS_ATTACHMENT" : {
					"type" : "boolean",
					"expression" : "from[TVC Collaboration First Message].to.format.hasfile"
				},
				"TVC_COLLAB_THREAD_IMPORTANT" : {
					"type" : "boolean",
					"expression" : "from[TVC Collaboration First Message].to.attribute[TVC Collaboration Important]"
				},
				"TVC_COLLAB_THREAD_LAST_MSG_SENT_FROM" : {
					"type" : "keyword",
					"expression" : "from[TVC Collaboration Last Message].to.owner"
				},
				"TVC_COLLAB_THREAD_LAST_MSG_SENT_TO" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.LastMessageSentToDataHandler"
				},
				"TVC_COLLAB_THREAD_LAST_MSG_SENT_CC" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.LastMessageSentCcDataHandler"
				},
				"TVC_COLLAB_THREAD_PRIVATE" : {
					"type" : "boolean",
					"expression" : "attribute[TVC Collaboration Private]"
				},
				"TVC_COLLAB_THREAD_SENT_FROM" : {
					"type" : "keyword",
					"expression" : "from[TVC Collaboration Thread Message].to.owner"
				},
				"TVC_COLLAB_THREAD_SENT_TO" : {
					"type" : "text",
					"expression" : "from[TVC Collaboration Thread Message].to.from[TVC Collaboration Sent To].to.owner"
				},
				"TVC_COLLAB_THREAD_SUBJECT" : {
					"type" : "keyword",
					"expression" : "attribute[TVC Collaboration Subject]"
				},
				"TVC_COLLAB_THREAD_SYSTEM_TAGS" : {
					"type" : "text",
					"className" : "com.technia.tvc.collaboration.discussion.elasticsearch.impl.SystemTagsDataHandler"
				}
			}
		}]]>
		</Schema>
	</ElasticSearch>

In order to specify the name of the index, <Endpoint> tag is used. Elasticsearch supports only lowercase characters.

<Endpoint>/tvc_collaboration_discussion</Endpoint>
Configurations for adding an additional custom field

In order to add custom fields, simply add them to the <Schema>..</Schema> json above and define a "type" and "className" or "expression". A custom class for a custom field should implement the following interface,

com.technia.tvc.collaboration.discussion.elasticsearch.ESDataHandler

For indexing a specific system tag, you need to implement the following abstract class and implement the method getKey() to specify the Tag that needs to be indexed.

com.technia.tvc.collaboration.discussion.elasticsearch.impl.IndexedSystemTagDataHandler
It’s important to restart the Application Server and Sync Service, if you delete the Index in ElasticSearch manually, otherwise the schema will not be set correctly.

3.7.2. Backup Sync Service

The call to index an object in elastic search is made as soon as the transaction is committed. However, there could be scenarios where the index fails due to network issues etc. So in order to cover those scenarios, there is a possibility to setup a backup sync service which will look for objects which are not indexed and will try to index them. Follow these steps to setup the service as a windows service.

  1. Copy the batch files from ../3dspace/WEB-INF/collaboration to your preferred local folder like C:/temp/ESSyncService

  2. Download NSSM (64 bit) and copy the .exe to the same folder above (C:/temp/ESSyncService)

  3. Rename the nssm.exe to TVC-ES-Indexing-Application.exe.

  4. Modify the setup.bat file with the correct path etc. and remove what is not applicable for your setup.

  5. Modify InstallEsIndexing.bat with the correct paths etc. The default service name is "TVC-ES-Indexing-Application-localhost" which can be changed as desirable.

  6. Now run the InstallEsIndexing.bat as administrator and the service will be installed.

In order to control certain aspects of the sync service, you need to create "indexing.properties" file in the same folder specified above (C:/temp/ESSyncService) with all the following properties.

Property Key values Description

tvc.esindexing.resourceRootDirectory

C:/webapp/3dspace/WEB-INF/tvc

specify the location of tvc

tvc.esindexing.threadpoolsize

5

specify the number of threads to be used for indexing

tvc.esindexing.thread.findlimit

5000

specify the number of non-indexed objects to search for at a time. If the threadpoolsize is 5 and findLimit is 5000 then each thread will index 1000 objects

tvc.esindexing.monitor.rate

5

specify (in secs.) the rate at which the service should look for non-indexed objects in the database

tvc.esindexing.user

creator

specify the context user

tvc.esindexing.password

specify the context user password

tvc.esindexing.forcereindex

true or false

if TRUE then the service will re-index already indexed objects as well, otherwise only non-indexed objects will be indexed

The server where the sync service needs to be setup should have a 64-bit JDK. It will not work with a 32-bit JDK.

In order to control logging, you need to create "logging.properties" file with something like this. Also update the location where the log file should be created.

# Configure root logger
log4j.rootLogger=ERROR

log4j.logger.com.technia.tvc = INFO, TVC_APPENDER, TVC_CONSOLE_APPENDER

log4j.appender.TVC_CONSOLE_APPENDER=com.technia.tvc.log4j.ConsoleAppender
log4j.appender.TVC_CONSOLE_APPENDER.layout=com.technia.tvc.log4j.PatternLayout
log4j.appender.TVC_CONSOLE_APPENDER.layout.ConversionPattern=[TVC-ESINDEXING] [%t]%x %5p %d{ISO8601} (%F:%L) - %m%n

# Configure TVC_APPENDER
log4j.appender.TVC_APPENDER=com.technia.tvc.log4j.DailyRollingFileAppender
log4j.appender.TVC_APPENDER.File=C:/Temp/syncService/logs/TVC-Esindexing456.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.TVC_APPENDER.layout=com.technia.tvc.log4j.PatternLayout
log4j.appender.TVC_APPENDER.layout.ConversionPattern=[TVC-ESINDEXING] [%t]%x %5p %d{ISO8601} (%F:%L) - %m%n

3.7.3. Delete Use Case

Today, deletion of "TVC Collaboration Threads" is not available as part of the standard product. However, there could be use cases where you would like to delete old (older than 90 days) thread objects to get rid of old data like using a cron job. ElasticSearch indexing supports this use case and in order to remove/delete the objects from elastic search index, you need to call the elastic search in the following way. This should be called after the objects are successfully deleted from the database i.e after the transaction is committed in the databse. Also, if the deletion request to elastic search fails due to network issue, the request will be persisted in the database and the backup sync service will try to remove those objects from elastic search index again until successful.

ElasticSearch.getInstance().indexer().remove(Identifier physicalId);
ElasticSearch.getInstance().indexer().removeAll(List<Identifier> physicalIds);

3.8. 3DDashboard Widgets

Collaboration True widgets can be configured through the widget preferences in the administrator’s dashboard. It runs directly inside the 3DDashboard widget iframe. The following widgets URL can be used to configure myspace and panel as a custom Application in 3DDashboard.

  • Myspace : <server-url>/webapps/tvc/collaboration/tvcCollaborationMyspaceWidget.html

image

  • Panel : <server-url>/webapps/tvc/collaboration/tvcCollaborationPanelWidget.html

image

Refer 3DDashboard chapter for configuring the widget app.

3.8.1. Drag and drop support in panel widget

The collaboration panel needs a context object to show the related sets. If there is no context object, the panel widget will show the message to drop an object.

Drop messages can be changed and internationalized through the widget string resource file which exists under the <webapps>\tvc\collaboration\asset\nls location.

Users can drag an object from TVC Widget, Helium True Widget, and DS OOTB widgets and drop it over the TVC Collaboration Panel widget to refresh the panel with the dropped object as context.

image

3.8.2. Collaboration Widget Publish/Subscribe

TVC collaboration panel widget can be subscribed for other widgets to refresh the panel widget by clicking the other widget’s table link. Users need to add the comma separate widget ids in the Widget to Subscribe field on the Edit preferences page.

It can also be published for other widgets, so on clicking the context object link in collaboration widgets, respected widgets will be refreshed. For such a case, the user needs to add the widget id of the panel widget into the Widget to Subscribe field in the Edit preferences page of other widgets.

image

4. Discussions

The discussion functionality allows you to discuss around any type of object in Enovia. The discussion functionality is located in a side panel on the right side of the page content. This allows the users to discuss around an object while still being able to navigate around the different pages of an object.

4.1. Discussion panel setup

The panel that contains the discussion functionality is located in the right side of the application. It is injected on the side of the page content so It will stay visible even when navigating around the different pages of an object. The panel is collapsed by default and will be expanded on click.

image

After clicking on the panel it will be expanded towards the left side and show the discussions for the object that you are currently in context of.

image

If you disabled the discussion functionality for some object types by configuration the panel will be greyed out and the user will not be able to expand it.

4.2. Single Discussion Load

If there is single set in the set list and user want to load the details of set on load of panel or on switching the panel tab then following system property need to be set as true (default:false).

Property Description Default Value

tvc.collaboration.panel.showSingleSet

to load the details of set if there is only one in the list

false

In Single set load, set id priority will be in following order:

  1. Passed setId in panel load option

  2. Set Id If there is single set in the set list and property is true.

  3. Cached working set Id in session storage

4.3. Viewing object discussions

When the user is standing in context of an object that has support enabled for discussions it will be possible to expand the discussion panel in order to view and/or create discussions. The default view of the panel will show a toolbar in the top and below that a list of all the discussions that has been started around the context object.

image
Figure 1. Listing context object discussions

Each discussion in the list is clickable. The entire discussion will be loaded when a user clicks on it. See next chapter for more details on that.

4.4. Viewing a discussion

When a discussion in the list has been clicked it will load the entire discussion in the panel. In this view the user will be able to see all the messages in the discussion and also have the ability to reply to it.

image
Figure 2. Showing a discussion

4.5. Discussion Command

Command like ShowWatchers, ToggleWatch, Reply and customize command are configurabale. possible command position are HEADER, FOOTER , BOTH. By defalut command, Reply position are BOTH.

public class DiscussionReply extends Command {
	...
    public DiscussionReply(MenuContext ctx) {
		...
        this.setPosition(Position.BOTH);
		...
    }
	...
}

4.6. Creating a new discussion

In order to create a new discussion you use the action in the top toolbar (plus sign). That will launch a form that allows you enter the data needed to create a new discussion.

image
Figure 3. Creating a new discussion

The following data can be entered:

  • The subject of the discussion

  • A flag that indicates if the discussion is important or not (visual indication)

  • A list of recipients. If this is set then the message will be "private" and only visible to these users. Otherwise it will be public and visible to everyone that has access to this object.

  • A set of user "tags". Currently this is only used for display but in coming releases it could be used for loading discussions based on tags.

  • Message content (rich text or plain text)

  • Attachments

4.7. Replying to a discussion

A user can reply to a discussion using the reply button located at the top or bottom of the discussion. The reply form is the same as the one you use when you create a new discussion but you are not able to set a subject (see chapter above on creating a new discussion)

image
Figure 4. Replying to a discussion

4.8. Closing a discussion

When a discussion is no longer required to continue, the user can close the discussion using the close button located at the top of the discussion. Once the discussion is closed, replying to a discussion is not possible. By default, only the owner of the discussion can access the close discussion command.

image
Figure 5. Closing a discussion

4.9. Opening a discussion

In some cases, the user may need to reopen the discussion. In those cases, the user can reopen the discussion using the open button located at the top of the discussion. By default, only the owner of the discussion can access the open discussion command.

image
Figure 6. Opening a discussion

4.10. Printing a discussion

To print a discussion that happened in any context, the user can use the print button located at the top of the discussion.

image
Figure 7. Printing a discussion

4.11. Deleting a discussion

When a discussion is no longer required, the user can delete the discussion using the delete button located at the top of the discussion.

Once the discussion is deleted, undo is not possible. By default, the only owner of the discussion can access the delete discussion command.

image
Figure 8. Deleting a discussion

4.12. Discussion Commands Configuration

Users can configure the discussion behaviour by changing the below properties in the string resource file.

Property Description Default Value

tvc.collaboration.thread.status.active

Active discussion status

Active

tvc.collaboration.thread.status.inactive

Inactive discussion status

Inactive

tvc.collaboration.discussion.toaster.delete.success

Delete success message

Discussion is deleted successfully

tvc.collaboration.discussion.toaster.close.success

Close success message

Discussion is closed successfully

tvc.collaboration.discussion.toaster.open.success

Open success message

Discussion is reopened successfully

The following system properties are introduced to control commands behaviour.

Property Description Default Value

tvc.collaboration.discussion.command.delete

Delete discussion command

false

tvc.collaboration.discussion.command.open

Open discussion command

false

tvc.collaboration.discussion.command.close

Close discussion command

false

tvc.collaboration.discussion.command.print

Print discussion command

true

4.13. Create/Reply Discussion Form Configuration

Using tvc.properties create/reply discussion form fields can be configured to be hidden except Subject and Message fields.

Property Description Default Value

tvc.collaboration.showPrivate

To show Private Filed

false

tvc.collaboration.showImportant

To show Important Filed

true

tvc.collaboration.showToRecipient

To show To Recipient Filed

true

tvc.collaboration.showCcRecipient

To show Cc Recipient Filed

true

tvc.collaboration.showWatcher

To show Watcher Filed

true

tvc.collaboration.showUserTags

To show User Tags Filed

false

tvc.collaboration.showContextObjects

To show Context Objects Filed (shown for multiple context objects)

true

tvc.collaboration.showDropzone

To show Dropzone Filed

true

tvc.collaboration.focus.message.reply

To focus message field on reply

false

4.14. Configuration

The ability to discuss around objects can be configured on the server. This is done in a file called TVCDiscussionConfig.xml that is placed in /WEB-INF/classes/TVCDiscussionConfig.xml. If you skip to have this configuration file the users will be able to discuss around ANY object in Enovia with default settings.

Below is an example configuration file that shows the supported elements and their location.

<DiscussionConfigs>
    <TabOrder>1</TabOrder>
    <ConfiguredTypes>
        <Type derived="false" status="disabled">type_MechanicalPart</Type>
        <Type derived="false" status="expand" if="$&lt;context.user.isassigned[role_Designer]&gt;">type_HardwarePart</Type>
        <Type derived="false" status="disabled">type_HardwarePart</Type>
        <Type derived="false" status="enabled">type_NutPart</Type>
        <Type derived="false" status="enabled" className="com.technia.tvc.collaboration.discussion.model.dao.enovia.config.builtin.DiscussionConfigType">type_NutPart</Type>
    </ConfiguredTypes>
    <DiscussionConfig for="Part" if="attribute[Usage] != Abc">
        <TagHandler className="..."/>
        <Tags>
            <Object>
                <Tag select="type" key="kind"/>
                <Tag select="name" key="identifier"/>
                <Tag select="revision" key="revision"/>
                <Tag select="current" key="state"/>
            </Object>
            <User>
                <!-- user specific tag. used for custom context action and filter. -->
                <Tag key="xfav" value="false" />
            </User>
        </Tags>
        <ContextInfoResolver>
            <Macro>${TYPE}, ${NAME} : ${REVISION}</Macro>
            <URL>/common/emxNavigator.jsp?objectId=${OBJECTID}</URL>
        </ContextInfoResolver>
        <Cloud>true</Cloud>
        <Toolbar>tvc:menu:tvx:collaboration/CustomToolbar.xml</Toolbar>
        <Filter ref="tvc:collaborationfilter:tvx:collaboration/Product.xml" />
    </DiscussionConfig>
    <ObjectIdResolver/>
    <OrganizationResolver/>
    <ContextInfoResolver/>
    <DiscussionConfigSorter className="com.technia.tvc.collaboration.discussion.model.dao.enovia.config.DefaultDiscussionConfigSorter" />
    <DiscussionPanelThreadActions>tvc:menu:tvx:collaboration/CustomMenu.xml</DiscussionPanelThreadActions>
    <InboxThreadActions>tvc:menu:tvx:collaboration/CustomMenu.xml</InboxThreadActions>
    <TagIndexFieldMapping>
        <Tag key="office" fieldName="CUSTOM_TAG_FIELD_OFFICE" />
    </TagIndexFieldMapping>
</DiscussionConfigs>

4.14.1. TabOrder

This element defines the component position in sidepanel and myspace. The text value should be integer and Components are arranged in ascending order.

4.14.2. Tab Persistence

To open the collaboration panel for a context object with previously active component tab or set, browser storage is used.

This behavior could be controlled by using different type of storage which is handled by following property.

Property Key values Description

tvc.collaboration.panel.storage.preference

memory(default)

persist only for panel instance

local

persist until the user manually clears the browser cache or until your web app clears the data

session

persist until the window or tab is closed.

4.14.3. ConfiguredTypes

The <ConfiguredTypes> tag allows configuration of component’s visibility for different types in the system. The child elements for this are <Type>

4.14.4. Type

This element allows you to control the collaboration behavior for a specific type. Following are the supported attributes for Type element.

Attribute Description Default Value

status

enabled, disabled, hidden, expand (enabled and automatically opened side panel) or none

none

derived

whether status applicable to derived types

true

if

whether configuration is applicable

className

class extending com.technia.tvc.collaboration.core.model.dao.enovia.config.panel.DefaultConfigType for custom logic

Status Persistence

Following system property can be used to persist the panel status of a context object type for a user.

Proeprty Description Default Value

tvc.collaboration.panel.status.persist

Persist the status through the data object of a person

false

Default panel status for non-configured type is "disabled".

Following property can be used to change the behaviour.

Property Description Default Value

tvc.collaboration.discussion.panel.status

hidden

disabled

4.14.5. DiscussionConfig

The <DiscussionConfig> element is used if you want to configure the discussion behavior for a type of object. You can have any amount of this element in your configuration. This is currently used in order to configure system generated tags that are saved on each discussion.

4.14.6. TagHandler

Allows you to point out a class that will have the logic for writing system generated tags on discussions. The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.tag.TagHandler.

4.14.7. Tags

This element allows you to generate system tags added to discussions. You can add any amount of child elements with the name <Tag>. On that element the "select" attribute is used for adding a select expression for fetching values to save and the "key" element specifies the key to save it on. All tags are saved together as a JSON string in an attribute.

4.14.8. ContextInfoResolver

This element is applicable to a single CollaborationConfig (i.e DiscussionConfig or WorkflowConfig) and also to all CollaborationConfigs. This element can be used to configure custom settings for the context information related to an object (used to display a link to the context object for a discussion/workflow).

The child element <Macro> is used to configure the "display name" and the <URL> element is used to configure the URL to the context object.

The child element <FileInfoResolver> is used to configure the File Actions displayed in the inbox if the context object holds files. If TVC Office component is available, the actions behave similar to column type "actions".

Default implementation resolves display name from <Macro> ${type}, ${name} : ${revision}</Macro> and context URL from <URL> /common/emxNavigator.jsp?objectId=${OBJECTID}</URL>.

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.context.ContextInfoResolver.

Following properties can be used to configure the behavior of context path.

Property Description Default Value

tvc.collaboration.contextpath.popup

Whether context path should be opened popup

false

tvc.collaboration.contextpath.popup.width

Width of context path popup window in pixel

900

tvc.collaboration.contextpath.popup.height

Height of context path popup window in pixel

700

tvc.collaboration.helium.route

Whether to use helium route when running in Helium

true

4.14.9. TagIndexFieldMapping

This element makes it possible to map a specific tag against a field that is indexed in Exalead. This feature is optional but should be used when you have advanced filters against tags since the performance is improved.

<TagIndexFieldMapping>
  <Tag key="office" fieldName="CUSTOM_TAG_FIELD_OFFICE" />
</TagIndexFieldMapping>

To get Exalead to index this field you also have to add the following to your exalead index configuration next to the other fields that are indexed for threads.

<FIELD name="CUSTOM_TAG_FIELD_OFFICE"
	   select="program[TVCCollaborationIndex -method getIndexedSystemTag ${OBJECTID} 'office']"
	   type="STRING" />

4.14.10. Cloud

Configures if clouds should be generated. Clouds are used to label messages with one or more entities (other than the context object). Read more about Clouds in later chapters.

4.14.11. ObjectIdResolver

This element is required only when custom parameters instead of objectId passed to load discussions. Allows you to point out a class that will have the logic for returning objectId from the parameters.

Default implementation resolves objectId based on type, name, revision and vault values separated by &. (type=Part&name=10001&revision=1&vault=Production).

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.dao.enovia.config.ObjectIdResolver.

4.14.12. OrganizationResolver

This element allows to return the Organization of the user, which can be used to show along with user name is Collaboration component. When there are multiple Organizations returned for the user, multiple entries will be shown in the autocomplete for the given user each for each organization.

Default implementation resolves Organizations based on Member relationship to Organization by "Organization Name" attribute value.

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.dao.enovia.config.OrganizationResolver.

This element also supports an attribute called "select" which can be selectable to get Organization value.

<OrganizationResolver select="attribute[Organization Name]"/>.

From/Sender user organization is resolved using the implementation of OrganizationResolver’s getSenderOrganizations.

This property has to be enabled for Organization

tvc.collaboration.showOrganization=true.

Following properties can be used to tune autocomplete field.

Property Description Default Value

tvc.collaboration.showOrganization

To enable Organizations inside collaboration component

false

tvc.collaboration.autocomplete.startWith

Whether search criteria should match starting instead of contains

false

tvc.collaboration.autocomplete.minLength

No. of characters after autocomplete initiated

2

tvc.collaboration.autocomplete.loadThrottle

No. of milliseconds after autocomplete initiated

400

tvc.collaboration.autocomplete.caseSensitive

Whether search criteria should be case sensitive

false

tvc.collaboration.autocomplete.clear.client.cache

Whether to clear existing cached search result

false

tvc.collaboration.autocomplete.loadOnFocus

To preload and open the assignee list on focus

false

public class TestOrganizationResolver extends DefaultOrganizationResolver {

    @Override
    public List<Organization> getOrganizations(String user) throws TVCException {
        return super.getOrganizations(user);
    }

    @Override
    public List<Organization> getSenderOrganizations(OrganizationContext context) throws TVCException {
        //context contains Message, previously resolved Organizations, contextIds and Env
        return super.getSenderOrganizations(context);
    }
}

image

4.14.13. PersonSuggestionsResolver

This element is applicable to a single CollaborationConfig (i.e DiscussionConfig or WorkflowConfig) and also to all CollaborationConfigs.

This element is used to get the user Suggestions for auto complete field along with the recently selected users.

The attribute select is used to configure expression which will be executed for context ids to get the person suggestions.

<PersonSuggestionsResolver className="tvc.collaboration.person.suggestions.TestPersonSuggestionsResolver" select="context.user"/>

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.person.suggestions.PersonSuggestionsResolver.

This property can be used for disabling the saving and displaying the suggestions. It is true by default.

tvc.collaboration.autocomplete.suggestions.enabled=true.

Following properties can be used to tune suggestions for autocomplete field.

Property Description Default Value

tvc.collaboration.autocomplete.suggestions.count

Max number of suggestions to be displayed

5

tvc.collaboration.autocomplete.search.fields

search criteria should based on the field of the search result. value is comma separated field list such as "label,value"

label

public class TestPersonSuggestionsResolver extends DefaultPersonSuggestionsResolver {

    @Override
    public List<Organization> getSuggestions(PersonSuggestionsData data){
        return super.getOrganizations(data);
    }

}

TVCDiscussionConfig.xml

<DiscussionConfigs>
...
    <DiscussionConfig for="type_Part">
        <PersonSuggestionsResolver select="owner"/>
    </DiscussionConfig>

    <PersonSuggestionsResolver select="context.user"/>
...
</DiscussionConfigs>

4.14.14. ContextInfoResolver

This element is applicable to a single CollaborationConfig (i.e DiscussionConfig or WorkflowConfig) and also to all CollaborationConfigs. This element can be used to configure custom settings for the context information related to an object (used to display a link to the context object for a discussion/workflow).

The child element <Macro> is used to configure the "display name" and the <URL> element is used to configure the URL to the context object.

The child element <FileInfoResolver> is used to configure the File Actions displayed in the inbox if the context object holds files. If TVC Office component is available, the actions behave similar to column type "actions".

Default implementation resolves display name from <Macro> ${type}, ${name} : ${revision}</Macro> and context URL from <URL> /common/emxNavigator.jsp?objectId=${OBJECTID}</URL>.

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.core.model.context.ContextInfoResolver.

Following properties can be used to configure the behavior of context path.

Property Description Default Value

tvc.collaboration.contextpath.popup

Whether context path should be opened popup

false

tvc.collaboration.contextpath.popup.width

Width of context path popup window in pixel

900

tvc.collaboration.contextpath.popup.height

Height of context path popup window in pixel

700

tvc.collaboration.helium.route

Whether to use helium route when running in Helium

true

4.14.15. DiscussionConfigSorter

This element allows configuration of custom sort program for DiscussionConfigs.

The complete name of the class to use is added to the attribute called "className". That class has to implement the interface com.technia.tvc.collaboration.discussion.model.dao.enovia.config.DiscussionConfigSorter.

4.14.16. Rich Text Editor

A Rich Text Editor can be enabled instead of the standard message field when creating or replying to discussions. This editor allows the user to write messages using "rich text", enabling more advanced formatting.

The editor is enabled globally in the TVCDiscussionConfig.xml file. The default and preferred editor is called TinyMCE (https://www.tinymce.com/) but there is also legacy support for the editor called Trumbowyg (https://alex-d.github.io/Trumbowyg/)

When using the TinyMCE editor it is also possible to configure it by passing custom init settings. This is done using the optional <EditorSettings> element. Check the TinyMCE web page for documentation on valid settings.

i. The settings has to be valid JSON. See example below
ii. Rich Text Editor tinymce/trumbowyg is supported on Internet Explorer with Ver:11 and above
iii. TinyMCE V5.10.5 is used for tinymce editor.
<DiscussionConfigs>
...
    <RichTextEditor>
        <Editor>tinymce</Editor>
        <!-- <Editor>trumbowyg</Editor> -->
        <EditorSettings><![CDATA[{
          "height": 500,
          "menubar": false,
          "plugins": [
            "advlist autolink lists link image charmap print preview anchor",
            "searchreplace visualblocks code fullscreen",
            "insertdatetime media table contextmenu paste code"
          ],
          "toolbar": "undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
          "content_css": "//www.tinymce.com/css/codepen.min.css"
        }]]></EditorSettings>
    </RichTextEditor>
</DiscussionConfigs>

4.15. Cloud (Exalead powered feature)

Messages are often created in context of an object. It can later on be found when navigating to that particular object and then opening the discussion panel. The cloud concept allows labelling of messages with other entities. It can be other objects or fictive clouds.

Example: Let’s presume we are in the apparel industry. We are working with seasons and each season we launch a number of products which in turn consists of parts. When a message is created on a product it should be labelled with the season cloud. Messages created on a part should be included in both the product and season cloud. Once users navigate to the product or season they can see discussions related to the product/season cloud.

4.15.1. Cloud Locator

The Cloud Locator is used to define the cloud(s) a particular object is part of. It locates the clouds based on object ids.

The locator is used when creating new messages that have clouds enabled (done in the DiscussionConfig). For searching it also used when the "searchBase" is set to cloud. A Clouds Locator is required to be implemented and registered if using the cloud concept.

Cloud Locator interface:

public interface CloudLocator {
    Collection<Cloud> getClouds(Collection<Identifier> ids);
}

Note that the method takes a collection of ids. Messages can be created in context of several objects, hence the collection. The clouds returned should consider all the ids.

Example implementation:

public class ExampleCloudLocator implements CloudLocator {

    private static final Logger logger = Logger.getLogger(ExampleCloudLocator.class);

    @Override
    public Collection<Cloud> getClouds(Collection<Identifier> ids) {
        Collection<Cloud> clouds = new ArrayList<Cloud>();
        try {
            DataExtractor extractor = new DataExtractor()
                .addId(EnoviaIds.asList(ids))
                .addStatement(Statement.ID, Statement.TYPE, Statement.NAME);
            Result result = extractor.perform();
            Iterator<SelectedData> itr = result.iterator();
            while (itr.hasNext()) {
                SelectedData data = itr.next();

                String id = data.getSelectValue(Statement.ID);
                String type = data.getSelectValue(Statement.TYPE);
                String name = data.getSelectValue(Statement.NAME);

                if ("Product".equals(type)) {
                    clouds.add(Cloud.getIntance(id, String.format("Product %s", name)));
                } else if ("Part".equals(type)) {
                    clouds.add(Cloud.getIntance(id, String.format("Part %s", name)));

                    // Add to product cloud
                    Product product = getProduct(id);
                    if (product != null) {
                        clouds.add(Cloud.getIntance(product.getId(),
                                String.format("Product %s", product.getName())));
                    }
                }
            }

        } catch (TVCException e) {
            logger.error(e, e);
        }
        return clouds;
    }

    private Product getProduct(String partId) throws TVCException {
        // Code omitted
    }
}

Register the locator when application server starts in your TVC Plugin class:

public final class AcmePlugin extends TVCComponentBase {

    @Override
    public void init() {
        CloudService.setLocator(new ExampleCloudLocator());
    }
}

4.16. Subscriptions

When a discussion is created and sent to some users all of them are automatically subscribed (started watching) to the thread. Any reply to the thread is notified to all the subscribers/watchers of the thread.

Any time user can unsubscribe or stop watching the thread clicking the stop watching. Any time use can start watching the thread by clicking start watching.

image

Start watching / Stop watching action can be performed in inbox as well.

image

When a thread is replied number of watchers to the thread is shown. Clicking the watcher count will show all users watching the thread.

image

4.17. Single page mode

By default the reply form is opened in "popups". This is good for visibility since the form gets all focus. However this prevents the users from reading the previous replies while doing a new reply. So if it is more important to be able to see the entire discussion while gruntreplying we have added a new rendering mode called "single page mode". This will do the following two changes to the UI:

  1. The reply form will not be opened in "popups". It is instead rendered directly in the top of the discussion you are replying to (both inbox and side panel).

  2. All discussions are expanded/viewed directly in the same "page" when you click on them. (Instead of reloading the whole page with the clicked discussion)

This mode is enabled by setting the following parameter to true.

Property Description Default Value

tvc.collaboration.discussionpanel.mode.singlePage

Enable single page mode

false

4.18. Page Size

By default all notifications and collaboration objects are displayed under myspace and panel but we can limit the page size to limit the objects count on initial load by using following property:

Property Description Default Value Range

tvc.collaboration.inbox.pagesize

limit objects on initial load in myspace

100

10 to 500

tvc.collaboration.discussionpanel.pagesize

limit objects on initial load in panel

50

10 to 500

If there is more objects than the page size exist in system then on scroll next set of page is displayed.

4.19. Absence and Delegation

Users can updated their Absence dates and Delegatee to be assigned for any future active Workflow tasks during that period can be configured in Myspace’s Profile Action page.

image
Figure 9. Absence and Delegation Configuration

On selection of absent person for To, CC or assignee field, it will be reflected in red and tooltip will provide the absence related information.

image
Figure 10. Absent Person Selection

Property

Description

Default

tvc.collaboration.person.showAbsenceMessage

To show the absent person selection in red and tooltip for absence details

true

tvc.collaboration.person.profile.startDateInputTime

to add the start day input time on selected date

00:00:00

tvc.collaboration.person.profile.endDateInputTime

to add the end day input time on selected date

23:59:59

tvc.collaboration.person.delegation.cache

to use java object cache to fetch the person delegation information

true

5. Notifications

5.1. Receiving a notification

At some events that occur in the discussion functionality there are "notifications" sent out to users that are involved in a discussion in one or more ways. If someone starts a discussion around an object then the owner of that object will get a notification. Users will also get notifications when another user is replying to a discussion or message that user started.

These notifications are sent to the user instantly when an event is occurring. We have two ways of notifying the users, one called "Inline notifications" and one called "Web notifications". These work in the same way but are displayed in different ways.

5.1.1. Inline notification (default)

Inline notifications are rendered in the top right corner of the browser window when an event occurs.

image Inline notification

5.1.2. Web notification

Web notifications are displayed on the computer desktop, outside of the browser window. The benefit of this is that you will notice it even if your browser window is minimized. This is currently not supported in Internet Explorer and really old versions of the other browsers. Clicking the notification will show the browser window and open My Space.

image Web notification

5.2. Notification Handler

Notification Handler can be used to enable other type of notifications like Email, Log or custom implementations. Default is Web notification which is used to send real-time notification to browsers.

Notification Handler is supported in both Collaboration Configs. (DiscussionConfigs or WorkflowConfigs).

It can be built-in handlers like <Web/>, <Email> or <Log/> or class implementing interface "com.technia.tvc.collaboration.core.model.dao.enovia.config.NotificationHandler"

NotificationHandler contains isValidNotification method which needs to be overridden to avoid the notification for a specific condition.
<WorkflowConfigs> or <DiscussionConfigs>
...
    <Notification>
        <Web />
        <Email />
        <Log />
        <Handler className="com.technia.tvc.collaboration.core.model.dao.enovia.config.notification.LogNotificationHandler" />
    </Notification>
</WorkflowConfigs> or </DiscussionConfigs>

5.2.1. Email Notification Handler

When Email Notification Handler is enabled, notifications will also be sent out to the recipients and watchers via Emails. The email notifications will not be sent to the context person and it can be controlled by the following property.

Property Description Default Value

tvc.collaboration.notification.email.to.context_user

Set true for sending notification and false for blocking.

false

User Preference for Email Notification

Users can enable and disable the Email notification through the profile in Myspace. See Myspace Profile Section for details.

On enabling/disabling the Email Notification, it sets the preference_NotificationEmail property on the person admin object.

5.2.2. Email Templates

Email Notification requires the MX_SMTP_HOST is configured and users have valid email address.

Email Notification Handler uses templates configured in string resources.

Following is the default template property key used for discussion email notifications.

tvc.collaboration.discussion.email.template

Following are the MACROs that can be used as $(MACRO) in the email templates.

FROM_USER
FROM_EMAIL
TO_USER
TO_EMAIL
DATE
MESSAGE
CONTEXT_INFO

Example:

tvc.collaboration.discussion.email.template = <p>Hello $(TO_USER),</p><p>Following message created by $(FROM_USER)<$(FROM_EMAIL)> at&nbsp;$(CONTEXT_INFO)</p> \
<blockquote>$(MESSAGE)</blockquote> \
<pre><em><sub>This message is system generated and sent by TVC Discussion.</sub></em></pre>

6. My Space

When clicking on the icon in the global toolbar it will open something called "My Space" as a big overlay in the browser window. "My Space" contains a set of different functionality related to the user and the TVC Collaboration feature. Inbox and Profile are the default preconfigured "My Space" Actions. The functionality that is displayed on first load is called "Inbox" and is described in the next chapter. First load "My Space" Action can be rearranged TVCDiscussionConfig.xml.

6.1. Inbox

The inbox is a functionality used for displaying all notifications that a user has received. It has a layout that is similar to a mail application.

image

Figure: Inbox displaying notifications

6.1.1. Configuration

When MySpaceConfig is modified to include other "My Space" Actions. It is mandatory to include default Actions as well. Actions can be reordered. All Inbox derived actions can be repeated with different configuration but with unique action id. Actions can also be grouped under different labels.

<MySpaceConfig>
    <!-- short form -->
    <!-- <InboxAction/> -->
    <!-- custom inbox with custom id -->
    <!-- <InboxAction id="some-unique-action-id"/> -->
    <InboxAction>
        <Font>fa-inbox</Font>
        <Label>Inbox</Label>
        <Label locale="de">Posteingang</Label>
        <Label locale="sv">inkorg</Label>
        <Header>Inbox</Header>
        <Header locale="de">Posteingang</Header>
        <Header locale="sv">inkorg</Header>
        <GroupLabel>Other</GroupLabel>

        <!-- optional advanced feature configurations. more details below. -->
        <ThreadActions className="com.technia.tvc.collaboration.core.test.TestDPLocalThreadActionsProvider">tvc:menu:tvx:collaboration/CustomMenu.xml</ThreadActions>
        <Filter ref="tvc:collaborationfilter:tvx:collaboration/Inbox.xml"></Filter>
        <ContextMenu ref="tvc:menu:tvx:collaboration/CustomMenu.xml"></ContextMenu>
        <Resources>
            <JavaScript src="/tvx/js/custom.js"></JavaScript>
            <StyleSheet src="/tvx/css/custom.css"></StyleSheet>
            <AjaxService service="customService"></AjaxService>
            <Template id="customTemplate" src="/tvx/template/custom.jsp"></Template>
        </Resources>
    </InboxAction>
</MySpaceConfig>

6.2. ToMe (Exalead powered feature)

"ToMeAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items sent to the context user as "To" Recipient.

image

6.2.1. Configuration

<MySpaceConfig>
    <ToMeAction></ToMeAction>
</MySpaceConfig>

6.3. CcMe (Exalead powered feature)

"CcMeAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items sent to the context user as "Cc" Recipient.

image

6.3.1. Configuration

<MySpaceConfig>
    <CcMeAction></CcMeAction>
</MySpaceConfig>

6.4. Unread (Exalead powered feature)

"UnreadAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are not read by the context user.

image

6.4.1. Configuration

<MySpaceConfig>
    <UnReadAction></UnReadAction>
</MySpaceConfig>

6.5. System Notification (Exalead powered feature)

"SystemNotificationAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are system generated. System generated Inbox items are identified using System Tags. Refer the consecutive chapters for configuration.

image

6.5.1. Creating Message using API

Other than user generated, messages can be created in result of system event or trigger actions.

DiscussionDAOFactory factory = DAOFactory.getInstance().getDiscussionFactory();
DiscussionConfigDAO configDAO = factory.getConfigDAO();
MessageDAO messageDAO = factory.getItemDAO();

Message message = new Message();
message.addContextId(configDAO.resolveId("14668.27509.6640.54321"));
message.setFrom(factory.getPersonDAO().read("Adam Sandler"));
message.setImportant(true);
message.setSubject("Fastener Part-EV-0000020-A released");
message.setMessage("Fastener Part-EV-0000020-A released using ECO Eco-00003");
message.setRepliable(false);

//sample tag to denote system notification
message.addSystemTag(Tag.getIntance("event", "trigger"));
//message.addSystemTag(Tag.getIntance("Event", "system"));

message.addRecipient(new Recipient(RecipientType.TO, new Person("Jennifer Aniston")));

Environment env = RequestUtils.newEnv(request);
env.put(Message.SANITIZE_HTML, false); //default is true

messageDAO.create(message, env);
Collection<Notification> notifications = messageDAO.getNotifications(message.getId(), env);
factory.getSetDAO().notify(new NotificationContext.Builder(ctx).setNotifications(notifications).build());

6.5.2. Configuration

SystemNotficationAction should be configured with Filter (see this chapter) with invisible FilterItem which matches the above generated System tag to filter System Notifications.

Other Inbox Actions as well should be configured with Filter which excludes System Notifications.

<MySpaceConfig>
    <SystemNotificationAction>
       <Filter applyOnLoad="true" showOnLoad="true" visibleRowCount="1">
           <Row visible="false">
               <FilterItem>
                   <Operator>EQUAL</Operator>
                   <LogicalOperator>OR</LogicalOperator>
                   <MapsTo>
                      <Tag>
                        <Name>event</Name>
                      </Tag>
                   </MapsTo>
                   <Values>
                       <Value selected="true">
                          <SubmitValue>trigger</SubmitValue>
                          <DisplayValue>trigger</DisplayValue>
                       </Value>
             </Values>
                   <UIControl>text</UIControl>
               </FilterItem>
           </Row>
       </Filter>
   </SystemNotificationAction>

   <!-- System Notifications to be excluded in other Inbox Actions -->
   <InboxAction>
       <Filter applyOnLoad="true" showOnLoad="true" visibleRowCount="1">
           <Row visible="false">
              <FilterItem>
                 <Operator>NOT_EQUAL</Operator>
                 <LogicalOperator>AND</LogicalOperator>
                 <MapsTo>
                     <Field>
                        <Name>TVC_COLLAB_THREAD_SYSTEM_TAGS</Name>
                     </Field>
                 </MapsTo>
                 <Values>
                     <Value selected="true">
                         <SubmitValue>event</SubmitValue>
                         <DisplayValue>event</DisplayValue>
                     </Value>
                 </Values>
                 <UIControl>text</UIControl>
             </FilterItem>
          </Row>
       </Filter>
    </InboxAction>
</MySpaceConfig>

6.6. Followup (Exalead powered feature)

"FollowupAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are followed by the context user.

Following Inbox items using Context Menu:

image

Followup showing followed item by the context user:

image

6.6.1. Configuration

<MySpaceConfig>
    <FollowupAction></FollowupAction>
</MySpaceConfig>

6.7. Deleted (Exalead powered feature)

"DeletedAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are deleted using delete context command by the context user.

image

6.7.1. Configuration

<MySpaceConfig>
    <DeletedAction></DeletedAction>
</MySpaceConfig>

6.8. Sent (Exalead powered feature)

"SentAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are sent by the context user.

image

6.8.1. Configuration

<MySpaceConfig>
    <SentAction></SentAction>
</MySpaceConfig>

6.9. Junk (Exalead powered feature)

"JunkAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are marked junk using junk context command by the context user.

image

6.9.1. Configuration

<MySpaceConfig>
    <JunkAction></JunkAction>
</MySpaceConfig>

6.10. Archive (Exalead powered feature)

"ArchiveAction" is a predefined custom Inbox Actions which depends on Exalead that can used in "My Space". This action shows the inbox items which are marked archive using archive context command by the context user.

image

6.10.1. Configuration

<MySpaceConfig>
    <ArchiveAction></ArchiveAction>
</MySpaceConfig>

6.11. Profile

The profile page has functionality for doing various types of actions related to the user. It is used for uploading an avatar image that will be displayed in discussions and for controlling user preferences.

image

Figure: Profile Page

6.11.1. Configuration

<MySpaceConfig>
    <ProfileAction></ProfileAction>
</MySpaceConfig>

6.12. External

"ExternalAction" is a predefined custom Inbox Actions which can used in "My Space". This action can be used to host External web application inside iframe of Collaboration component.

Bing.com as MySpace Action:

image

6.12.1. Configuration

<MySpaceConfig>
    <ExternalAction id="bing">
        <Label>Bing</Label>
        <Font>fa-search</Font>
        <Header>Bing</Header>
        <Href>http://bing.com</Href>
    </ExternalAction>
    <ExternalAction id="verge">
        <Label>Verge</Label>
        <Header>The Verge</Header>
        <Href>http://www.theverge.com/</Href>
    </ExternalAction>
</MySpaceConfig>

6.13. Inbox Context Actions

Inbox will have following context actions which can be applied on multiple selected Inbox items.

Action Description

Mark as Read

Marks the context selected Inbox item as read

Mark as unread

Marks the context selected Inbox item as not read

Follow up

Marks the context selected Inbox item as being followed

Unfollow up

Removes the context selected Inbox item being followed

Categorize

Contains sub actions to add categories and clear categories for the selected inbox item

Delete

Moves the context selected Inbox item to deleted inbox

Junk

Moves the context selected Inbox item to junk inbox

Archive

Moves the context selected Inbox item to archive inbox

Move to Inbox

Moves the context selected item to inbox again

Below properties can be configured to show the context actions like Delete, Archive, Junk and Move to Inbox

Property Description Default Value

tvc.collaboration.inbox.context.command.delete

Whether to show Delete context command

false or true when <DeleteAction> configured

tvc.collaboration.inbox.context.command.junk

Whether to show Junk context command

false or true when <JunkAction> configured

tvc.collaboration.inbox.context.command.archive

Whether to show Archive context command

false or true when <ArchiveAction> configured

tvc.collaboration.inbox.context.command.moveToInbox

Whether to show Move To Inbox context command

false or true when <DeleteAction> or <JunkAction> or <ArchiveAction> configured

image

Category’s label can be changed using "All Categories…​" context action.

image

6.13.1. Custom Context Action configuration

It is possible to add additional context action commands to Inbox Context Menu. This customization can be done with xml configuration.

The additional context actions for the specific inbox can be configured like this (placed inside a <InboxAction> or something that is derived from that).

<MySpaceConfig>
    <InboxAction>
        <ContextMenu ref="tvc:menu:tvx:collaboration/CustomContextMenu.xml"></ContextMenu>
  </InboxAction>
</MySpaceConfig>

image

6.13.2. Configuration element details

All the different configuration elements share the same abilities of configuration. You can ADD commands using a TVC XML menu configuration.

By adding a reference to a TVC XML menu configuration it is possible to add one or more actions. This uses the same menu configuration as other TVC (not everything supported though). Below is an example configuration:

<Menu>
    <Command>
        <Label>Welcome</Label>
        <Href>
            <![CDATA[
            javascript:alert('Hello!');
            ]]>
        </Href>
    </Command>
    <Command>
        <Label>Favourite</Label>
        <Href>
            <![CDATA[
            javascript:$inbox.updateUserTag($notification, 'xfav', 'true', false);
            ]]>
        </Href>
        <Setting name="enablerScript">
           <![CDATA[
            (function(notifications, name, value){
				return notifications.map(function(notification) {
                    var tag = notification.userTags.filter(function(tag) {
                    return tag.name === name;
                  });
                  return tag.length == 0 || tag[0].value== 'false';
                }).indexOf(true) > -1
            })(notification, 'xfav');
            ]]>
        </Setting>
        <Setting name="visibilityScript">
            <![CDATA[
            (function($inbox, id){
                return $inbox.getActionId() !== id;
            })($inbox, 'favourite-inbox');
            ]]>
        </Setting>
    </Command>
    <Command>
        <Label>Unfavourite</Label>
        <Href>
            <![CDATA[
            javascript:$inbox.updateUserTag($notification, 'xfav', 'false', $inbox.getActionId() === 'favourite-inbox');
            ]]>
        </Href>
        <Setting name="enablerScript">
            <![CDATA[
            $inbox.tagEnablerScript(notification, 'xfav', 'true');
            ]]>
        </Setting>
    </Command>
    <Command>
        <Label>Close</Label>
        <FontIcon>fa fa-pause</FontIcon>
        <Alt>Custom Close</Alt>
        <Href>
            <![CDATA[javascript:$context.closeDiscussion($event);]]>
        </Href>
		<AccessExpression><![CDATA['Test Everything' == context.user]]></AccessExpression>
		<Setting name="Status"> Active </Setting>
    </Command>
        <Command>
        <Label>Open</Label>
        <FontIcon>fa fa-play</FontIcon>
        <Href>
            <![CDATA[javascript:$context.openDiscussion($event);]]>
        </Href>
		<Setting name="Status"> Inactive </Setting>
    </Command>
    <Command>
        <Label>Delete</Label>
        <FontIcon>fa fa-times</FontIcon>
        <Href>
            <![CDATA[javascript:$context.deleteDiscussion($event);]]>
        </Href>
    </Command>
</Menu>

Use the <Image> element to control the action icon. Action command’s JavaScript executed on command click. Use the <Label> element for adding a text to the action.

AccessExpression

The setting is to control command visibility based on condition. Also, settings like AccessProgram and AccessFunction are supported.

Status

The setting to show a custom command based on discussion thread status. ex. If status is Active, the custom command is only visible when discussion thread status is Active.

Href

Here $inbox', '$notification' are script objects in scope when action command executes. other scope objects are '$event', 'notification', '$command'.

updateUserTag is method available on $inbox object.

  • first parameter is $notification (selected items)

  • second parameter is custom tag. it can be any valid tag name.

  • third parameter is custom tag value. it can be any valid tag value.

  • fourth parameter is boolean. 'true' will remove the selected item from view. 'false' does nothing.

    • ex: $inbox.getActionId() === 'favourite-inbox' evaluates to true or false. when true, selected item is removed from view. when current inbox is "favourite-inbox" and unfavouriting a item will remove the item from view.

<Command>
   <Href>
   <![CDATA[
    javascript:$inbox.updateUserTag($notification, 'xfav', 'true', false);
   ]]>
   </Href>
</Command>

<!-- or -->

<Command>
   <Href>
   <![CDATA[
    javascript:$inbox.updateUserTag($notification, 'xfav', 'true', $inbox.getActionId() === 'favourite-inbox');
   ]]>
   </Href>
</Command>

enablerScript

setting to a custom script function to show the command 'enabled' or 'disabled'. The function can use builtin scope objects like '$notification', 'notification', '$component' and '$inbox'.

tagEnablerScript is method available on $inbox object. returns true if any selected notifications contains the given tag and value.

TVC-2018.4.0 onwards, selection of multiple message has been supported for context action. So the first parameter will be an array of notification object and enabler script must be modified accordingly for proper behavior. Please refer the sample script.
  • first parameter is notification (selected items data which is an Array)

  • second parameter is custom tag. it can be any valid tag name.

  • third parameter is custom tag value. it can be any valid tag value.

<Command>
    <Setting name="enablerScript">
        <![CDATA[
            (function(notifications, name, value){
				return notifications.map(function(notification) {
                    var tag = notification.userTags.filter(function(tag) {
                    return tag.name === name;
                  });
                  return tag.length == 0 || tag[0].value== 'false';
                }).indexOf(true) > -1
            })(notification, 'xfav');
        ]]>
	</Setting>
</Command>

<!-- or -->

<Command>
    <Setting name="enablerScript">
    <![CDATA[
    $inbox.tagEnablerScript(notification, 'xfav', 'true');
    ]]>
    </Setting>
</Command>

visibilityScript

setting to a custom script function to change the visibility of the command. The function can use buitin scope objects like '$notification', 'notification', '$component' and '$inbox'.

below favourite command is visible for all inboxes except inbox named 'favourite-inbox'

<Command>
    <Setting name="visibilityScript">
    <![CDATA[
    (function($inbox, id){
       return $inbox.getActionId() !== id;
    })($inbox, 'favourite-inbox');
    ]]>
    </Setting>
</Command>

7. Structure Browser Integration

From a table page, you can add a column that will show an icon when a discussion exists for the object. Clicking on the icon brings up the discussion panel.

To enable this column in your table, add a column like shown below:

<Table>
    ...
    <Column>
        <Name>discussion-link</Name>
        <ColumnType>discussion-link</ColumnType>
    </Column>
</Table>

If the table is defined as a system table, use the setting "Column Type" to achieve the same. This "Column Type" can be used in Helium as well.

Below is a screenshot illustrating how the icon will appear in case there are discussions available.

image

8. External Web Application Integration

Just like in ENOVIA, Collaboration can be configured in external web applications:

8.1. Task 1 – Referring TVC Endpoint in Script tag

Web App should reference following TVC endpoint in Script tag

<html>
....
<body>
.....
<script data-collaboration="path/integration.js" type="text/javascript"
src="http://<server>/enovia/tvc/collaboration/tvcCollaborator.jsp"></script>
</body>
</html>

TVC Collaboration Integration URL:

tvc/collaboration/tvcCollaborator.jsp

Attribute data-collaboration="path/integration.js" points to hosting web application’s integration scripts.

8.2. Task 2 – Custom Collaboration Configurations

Web App invokes Collaboration script with configuration details.

//integration.js

// toolbarId: element id where notification icon injected
// contentId: element id where discussion panel injected as sibling
// env: optional custom information. can be obtained in server side custom code.

var collaboration = window.getCollaboration();
collaboration.config({ toolbarId: 'toolbar', contentId: 'main-content', env: { 'name': 'sk', 'external': 'skclusive' } });

8.3. Task 3 – Registering for Collaboration Ready Event

Web App registers for collaboration ready event which is passed with collaborator interface.

//integration.js
collaboration.ready(function ($collaborator) {
    console.log("collaboration is ready to be executed");

    $("#open-discussion").click(function () {
        var type = $("#type").val();
        var name = $("#name").val();
        var revision = $("#revision").val();
        var params   = "type=" + type + "&name=" + name + "&revision=" + revision;
        $collaborator.openCollaborationPanel([params]).then(function (value) {
			console.log("Discussion Panel is " + (!value ? "not " : "") + "opened for '" + params + "'");
        });
    });

   $("#close-discussion").click(function () {
	   $collaborator.closeCollaborationPanel().then(function () {
		console.log("closeDiscussionPanel successfully");
	   });
   });

   $("#open-myspace").click(function () {
	  $collaborator.openMySpace().then(function () {
		console.log("openMySpace successfully");
	  });
   });


   $("#close-myspace").click(function () {
	  $collaborator.closeMySpace().then(function () {
		console.log("closeMySpace successfully");
	  });
    });
});

9. Toolbar and Thread Actions

It is possible to add and remove action commands from both the discussion panel top toolbar and the actions that is related to the discussion thread (inbox and side panel). This can be done globally but also depending on the discussion context. This customization can be done with xml configuration AND/OR in JAVA code.

9.1. Global configuration

Thread actions can be configured globally and is done using the following elements (placed directly under the <DiscussionConfigs> element). This will then be used for ALL discussion configurations.

<DiscussionPanelThreadActions className="com.technia.tvc.collaboration.core.test.TestDPGlobalThreadActionsProvider">tvc:menu:tvx:collaboration/CustomMenu.xml</DiscussionPanelThreadActions>

<InboxThreadActions className="com.technia.tvc.collaboration.core.test.TestIBGlobalThreadActionsProvider">tvc:menu:tvx:collaboration/CustomMenu.xml</InboxThreadActions>

The first one will change the thread actions in the inbox and the second one for the side panel. See more details later in this document.

9.2. Local configuration

Thread actions and top toolbar actions can also be configured for different discussion configurations. This means you can have different actions depending on what configuration is used. You can also configure actions for the different My Space inboxes.

The top toolbar for the side panel is configured like this (placed inside a <DiscussionConfig> element).

<Toolbar className="com.technia.tvc.collaboration.core.test.TestToolbarProvider">tvc:menu:tvx:collaboration/CustomToolbar.xml</Toolbar>

Thread actions for side panel are configured like this: (placed inside a <DiscussionConfig> element)

<ThreadActions className="com.technia.tvc.collaboration.core.test.TestDPLocalThreadActionsProvider">tvc:menu:tvx:collaboration/CustomMenu.xml</ThreadActions>

Thread actions for an inbox are configured like this: (placed inside a <InboxAction> or something that is derived from that)

<ThreadActions className="com.technia.tvc.collaboration.core.test.TestDPLocalThreadActionsProvider">tvc:menu:tvx:collaboration/CustomMenu.xml</ThreadActions>

9.3. Configuration element details

All the different configuration elements share the same abilities of configuration. You can ADD commands using a TVC XML menu configuration AND/OR add or remove commands using Java code.

By adding a reference to a TVC XML menu configuration it is possible to add one or more actions. This uses the same menu configuration as other TVC (not everything supported though). Below is an example configuration:

<Menu>
    <Command>
        <Href>http://www.technia.com</Href>
        <Image>${ROOT_DIR}/xbom/xbl/images/create.gif</Image>
        <FontIcon>fa fa-thumbs-up</FontIcon>
        <Alt>tooltip here</Alt>
        <WindowHeight>400</WindowHeight>
        <WindowWidth>600</WindowWidth>
    </Command>
    <Command>
        <Href>javascript:alert('Clicked!');</Href>
        <FontIcon>fa fa-beer</FontIcon>
        <Label></Label>
        <Alt>tooltip here</Alt>
    </Command>
    <Command>
        <Label>Close</Label>
        <FontIcon>fa fa-pause</FontIcon>
        <Alt>Custom Close</Alt>
        <Href>
            <![CDATA[javascript:$context.closeDiscussion($event);]]>
        </Href>
		<AccessExpression><![CDATA['Test Everything' == context.user]]></AccessExpression>
		<Setting name="Status"> Active </Setting>
    </Command>
    <Command>
        <Label>Open</Label>
        <FontIcon>fa fa-play</FontIcon>
        <Href>
            <![CDATA[javascript:$context.openDiscussion($event);]]>
        </Href>
		<Setting name="Status"> Inactive </Setting>
    </Command>
    <Command>
        <Label>Delete</Label>
        <FontIcon>fa fa-times</FontIcon>
        <Href>
            <![CDATA[javascript:$context.deleteDiscussion($event);]]>
        </Href>
    </Command>
</Menu>

Use either the <Image> or <FontIcon> element to control the action icon. Action commands are by default opened as a popup but it is also possible to specify a JavaScript to be executed on click. Use the <Label> element for adding a text to the action (not used for top toolbar).

AccessExpression

The setting is to control command visibility based on condition. Also, settings like AccessProgram and AccessFunction are supported.

Status

The setting to show a custom command based on discussion thread status. ex. If status is Active, the custom command is only visible when discussion thread status is Active.

If more complex logic is needed to control what actions to show it is possible to use JAVA code to achieve this. It is done by adding an attribute called className that points to a custom JAVA class that extends one of the following classes:

com.technia.tvc.collaboration.core.model.dao.enovia.menu.DefaultToolbarProvider
com.technia.tvc.collaboration.core.model.dao.enovia.menu.DefaultDiscussionPanelThreadProvider
com.technia.tvc.collaboration.core.model.dao.enovia.menu.DefaultInboxThreadProvider

In the custom Java class it is possible to override the following method:

public Menu getMenu(MenuContext ctx)

That method will allow you to return the menu to use. By calling the super class method it is possible to get the default menu. The MenuContext object contains information like user, thread and context.

10. Advanced filter (Exalead powered feature)

It is possible to configure something called advanced filter in order to control the data loaded for this component. This type of filtering is able to load and filter on data that has been indexed by Exalead. This can be used to filter out both notifications in the inbox and discussion threads in the side panel. These filters can be hidden or visible so that the user can change values in the UI.

image

10.1. Filter configuration

In order to enable a filter you can reference a filter configuration in your TVCDiscussionConfig.xml file like this.

<Filter ref="tvc:collaborationfilter:tvx:collaboration/Product.xml" />

10.1.1. <Filter> element

The base element for the actual filter configuration is the <Filter> element. That one has a number of attributes that can be used to control some behavior for the entire filter.

<Filter showOnLoad="true" visibleRowCount="3" searchBase="cloud"
visible="true">
Attribute Description Default Value

showOnLoad

Show filter on load or not (if set to false the user will have to click the filter icon to show the filter

true

visibleRowCount

How many filter rows (rows described later) that will be showed on load. Visibility of rest can be toggled by the user.

searchBase

See chapter about Cloud

visible

Whether the filter should be shown or be hidden

true

10.1.2. <Row> element

Each filter can have any amount of rows. A row corresponds to a row in the filter user interface. Each row can have a label set and that will then be rendered to the far left. Each row can then have a single or multiple filter items. A row can be hidden using the "visible" attribute.

<Row visible="true">
    <Label>Anything</Label>
    <FilterItem>..</FilterItem>
    <FilterItem>..</FilterItem>
</Row>

10.1.3. <FilterItem>

A <FilterItem> represents an element that can be used to filter data (textfield, dropdown, button etc.). Each filteritem maps against one or multiple values that has been indexed by Exalead. Below is a complete example of a filteritem. Each element described after the example

<FilterItem>
    <Label>Test_label</Label>
    <Operator>NOT_EQUAL</Operator>
    <LogicalOperator>AND</LogicalOperator>
    <MapsTo>
        <Tag>
            <Name>category</Name>
        </Tag>
        <Field>
            <Name>THREAD_FIRST_MSG_SENT_FROM</Name>
        </Field>
    </MapsTo>
    <Values>
        <Value>
            <SubmitValue />
            <DisplayValue />
        </Value>
        <Value selected="true">
            <SubmitValue>production</SubmitValue>
            <DisplayValue>Production</DisplayValue>
        </Value>
        <Value>
            <SubmitValue>development</SubmitValue>
            <DisplayValue>Development</DisplayValue>
        </Value>
    </Values>
    <UIControl>dropdown</UIControl>
</FilterItem>
<Label>

It is used to define Label of FilterItem.

<Operator>

Can be used to control what operator to use for this field item. It is not mandatory to specify this.

  • EQUAL (default)

  • NOT_EQUAL

  • LESS_THAN

  • GREATER_THAN

<LogicalOperator>

Can be used to control what logical operator to use for this field item. Not mandatory to set this.

  • OR (default)

  • AND

<MapsTo>

This element is used to map this field against either tags or indexed fields in Exalead. In order to filter on tags one or more <Tag> elements are used and in order to filter on fields one or more <Field> elements are used.

<MapsTo>
    <Tag>
        <Name>category</Name>
    </Tag>
    <Field>
        <Name>THREAD_FIRST_MSG_SENT_FROM</Name>
    </Field>
</MapsTo>

user specific custom tag filter

<FilterItem>
    <Operator>EQUAL</Operator>
    <LogicalOperator>AND</LogicalOperator>
    <MapsTo>
        <Tag user="true">
            <Name>xfav</Name>
        </Tag>
    </MapsTo>
    <Values>
        <Value selected="true">
            <SubmitValue>true</SubmitValue>
            <DisplayValue>Favourite</DisplayValue>
        </Value>
    </Values>
    <UIControl>textbutton</UIControl>
</FilterItem>

image

<UIControl>

This element is used to control what type of UI control to use for this field. Supported values are:

DROPDOWN

Dropdown list with possibility to select ONE value.

MULTISELECTDROPDOWN

Dropdown list with possibility to select MULTIPLE values.

TEXT

Text field where user can enter any text

BOOLEANBUTTON

Yes/No buttons

TEXTBUTTON

Buttons containing custom values. (Each value renders as a button)

Some of these elements require you to configure the values that can be used. See next section

<Values>

This element is used to configure the allowed values for a <UIControl>. All UI controls except BOOLEANBUTTON and TEXT requires one or more values. Each value has a submit-value that is sent to the server and a display-value that is rendered for the user. A value can be configured to be default selected with the attribute "selected"

<Values>
    <Value>
        <SubmitValue>MHK</SubmitValue>
        <DisplayValue>MHK</DisplayValue>
    </Value>
    <Value selected="true">

Values can also be loaded through Java code like this.

<Values valueHandler="com.technia.tvc.example.valuehandler.ObjectIdValueHandler"/>

The value handler class needs to implement the com.technia.tvc.collaboration.core.model.dao.enovia.config.ValueHandler (deprecated) interface.

ValueHandler interface has been deprecated in favour of com.technia.tvc.collaboration.core.model.dao.enovia.config.FilterValueHandler.

Categories FilterItem Configuration

<FilterItem>
     <Label>Categories</Label>
     <Operator>EQUAL</Operator>
     <LogicalOperator>AND</LogicalOperator>
     <Type>category</Type>
     <UIControl>multiselectdropdown</UIControl>
</FilterItem>

11. Enovia Based Filter

It is possible to enable Exalead powered feature and filter in enovia mode by setting the following system property key:

tvc.collaboration.enovia.filter=true

We could have performamce issue in comparision of exalead mode as searching mechanism is different in case of enovia and exlead mode.

11.1. Filter configuration

In Enovia mode, the same configurations which are defined for Exalead Powered feature need to be done to use the filter and features such as Unread, Archive, To ME, CC Me, Followup etc…​

11.2. Index File configuration

Following fields must be added in Exalead Indexed Configuration file.

<BOTYPEFIELDS name="TVCCollaborationMXCriteria">
			<FIELD name="TVC_COLLAB_MX_NOTIFICATION_USER_NAME"
				select="to[TVC Collaboration Notification].from.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_WORKFLOW_CONTEXT_ID"
				select="to[TVC Collaboration Set].from.to[TVC Collaboration Object Proxy].from.id"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_CONTEXT_ID"
				select="to[TVC Collaboration Discussion].from.to[TVC Collaboration Discussion  Container].from.id"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_WORKFLOW_CONTEXT_PHYSICAL_ID"
				select="to[TVC Collaboration Set].from.to[TVC Collaboration Object Proxy].from.physicalid"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_CONTEXT_PHYSICAL_ID"
				select="to[TVC Collaboration Discussion].from.to[TVC Collaboration Discussion Container].from.physicalid"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_SENT_TO"
				select="from[TVC Collaboration Thread Message].to.from[TVC Collaboration Sent To].to.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_NOTIFICATION_UNREAD_USERS"
				select="to[TVC Collaboration Notification|attribute\[TVC Collaboration Unread Count\]>0].from.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_LAST_MSG_SENT_TO"
				select="from[TVC Collaboration Last Message].to.from[TVC Collaboration Sent To|attribute\[TVC Collaboration Recipient Type\]==0].to.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_LAST_MSG_SENT_CC"
				select="from[TVC Collaboration Last Message].to.from[TVC Collaboration Sent To|attribute\[TVC Collaboration Recipient Type\]==1].to.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_FOLLOWUP_USER_NAME"
				select="to[TVC Collaboration Followup].from.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_THREAD_FIRST_MSG_SENT_TO"
				select="from[TVC Collaboration First Message].to.from[TVC Collaboration Sent To].to.owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_TASK_ASSIGNED"
				select="from[TVC Collaboration Item].to[|attribute\[TVC Collaboration Task Status\]==Active].owner"
				type="STRING" />
			<FIELD name="TVC_COLLAB_MX_TASK_STATUS"
				select="from[TVC Collaboration Item].to[*|revision==last].attribute[TVC Collaboration Task Status]"
				type="STRING" />
		</BOTYPEFIELDS>

Multiple JPOs have been configured for mutiple filters for Exalead mode. These fields has been introduced to improve the performance on some extent by avoiding these JPO invocation.

12. Collaboration Panel Host based Visibility

Collaboration Panel can be used in variety of mode. ex: standalone, embedded, cross-domain. When using Helium in embedded mode, there could be double side panels. To avoid such situation and also to control which mode Collaboration Panel is enabled, following configurations can be used.

Property Description Default Value

tvc.collaboration.panel.classic.standalone

Enabling classic collaboration panel in standalone

(using tvcCollaborationPanelContent.jsp directly)

true

tvc.collaboration.panel.classic.embedded

Enabling classic collaboration panel in embedded

(default collaboration setup in ENOVIA or using tvcCollaborationPanelContent.jsp in iframes)

true

tvc.collaboration.panel.classic.crossdomain

Enabling classic collaboration panel in cross-domain

(using tvcCollaborationPanelContent.jsp in cross-domain iframe)

true

tvc.collaboration.panel.helium.standalone

Enabling helium collaboration panel in standalone

(default collaboration setup in Helium)

true

tvc.collaboration.panel.helium.embedded

Enabling helium collaboration panel in embedded

(using Helium in ENOVIA embedded mode)

false

tvc.collaboration.panel.helium.crossdomain

Enabling helium collaboration panel in cross-domain

(using Helium in ENOVIA cross domain embedded mode)

true

13. Additional Configurations

13.1. Date Format Configurations

Date formats inside collaboration user interface can be configured to display custom date format.

Collaboration component uses moment.js version 2.4.0 (http://momentjs.com/) for date/time display. So supporting formats documented here http://momentjs.com/docs/#/displaying/format/ and also a custom format "fromNow" equivalent to moment.js’s "fromNow" functionality.

Below properties can be configured to format the date used in sections of the collaboration component.

Property Description Default Value

tvc.collaboration.date.convert.userPreferenceTimeZone

Format the date depending on user preference timezone. Refer Figure 5.

true

tvc.collaboration.inbox.list.dateFormat

Date format used in Inbox items view

fromNow

tvc.collaboration.inbox.detail.dateFormat

Date format used in Inbox preview/detail view

YYYY-MMMM-DD, HH:mm:ss

tvc.collaboration.discussion.list.dateFormat

Date format used in sidepanel threads view

fromNow

tvc.collaboration.discussion.detail.dateFormat

Date format used in sidepanel thread detail view

fromNow

image
Figure 11. User Preference TimeZone

13.2. Miscellaneous Configurations

Additional property configurations for Collaboration

Property Description Default Value

tvc.collaboration.discussion.important.latest

Whether to show thread level important flag from latest message. (inbox and side panel)

By default, important flag of first message is shown.

false

tvc.collaboration.configured.locales

comma separated locales configured for collaboration string resources to avoid dual languages in view because of momentJS

Ex: en,sv,fr

en

tvc.collaboration.fallback.locale

Fallback locale If user’s preferred language is not within configured locales. This is to avoid dual languages in view because of momentJS.

Ex: sv

en

tvc.collaboration.initialavatar

Default user avatars that are based on the letter of their first name and last name and a random color. If disabled all users will instead have the same default avatar that is a static image.

true

tvc.collaboration.user.initial.char_count

To Display more than one character for default user avatars that are based on the first name and last name.

1

13.3. Additional Resources

Additional resources like JavaScript, CSS, AjaxService and Custom Template can be included in discussion component.

<JavaScript>, <StyleSheet>, <AjaxService> and <Template> element can be repeated to support multiple resource inclusion.

<Resources> configuration can be under configuration elements like <MySpaceConfig>, <MySpaceAction>, <TopbarConfig>, <DiscussionConfigs>, <DiscussionConfig>.

<DiscussionConfigs>

    <Resources>
        <StyleSheet src="/tvx/collaboration/layout-styles.css"/>
        <Template id="panel/layout" src="/tvx/collaboration/panel-layout.hbs"/>
    </Resources>

    <DiscussionConfig for="Part">
        <Resources>
            <JavaScript src="/tvx/js/custom.js" />
            <AjaxService service="messages" />
        </Resources>
    </DiscussionConfig>

    <TopbarConfig>
        <Resources>
            <StyleSheet src="/tvx/css/custom.css" />
            <Template id="customTemplate" src="/tvx/template/custom.jsp" />
        </Resources>
    </TopbarConfig>

    <MySpaceConfig>

        <Resources>
            <StyleSheet src="/tvx/collaboration/layout-styles.css"/>
            <Template id="myspace/layout" src="/tvx/collaboration/myspace-layout.hbs"/>
        </Resources>

        <InboxAction>
            <Resources>
                <AjaxService service="tvxCollaborationService"/>
                <AjaxService service="jsontest"/>
                <JavaScript src="/tvx/collaboration/inboxCustomScript.js"/>
                <StyleSheet src="/tvx/collaboration/inboxCustomStyles.css"/>
            </Resources>
            <ContextMenu ref="tvc:menu:tvx:collaboration/InboxContextMenu.xml"/>
        </InboxAction>

        <ProfileAction>
            <Resources>
                <StyleSheet src="/tvx/css/custom.css"/>
            </Resources>
        </ProfileAction>

    </MySpaceConfig>

</DiscussionConfigs>

Ex: inboxCustomScript.js

tvc.define('script.tvx', [ 'collaborator.ready!', 'ajax.tvxCollaborationService' ],

function($collaborator, tvxCollaborationService) {
	var tvx = {
        fakeDelete : function($event, $notification, $command, $inbox) {
            tvxCollaborationService.fakeDeletePromise().then(function(result) {
                $notification.addClass('fake-deleted');
                alert(result);
            });
        }
    };
    return tvx;
});

Ex: InboxContextMenu.xml

...

<Command>
	<Label>Fake delete (ajax example)</Label>
	<Href><![CDATA[
		javascript:(function($event, $notification, $command, $inbox) {
			tvc.require(['script.tvx'], function(tvx) {
			   tvx.fakeDelete($event, $notification, $command, $inbox);
			});
		}($event, $notification, $command, $inbox));
	]]></Href>
</Command>

...