Process document
The word “process document” specifically reports to a document stemming from a process template (workflow) which is built from generated element present in the Process management. The process document does not start from the browsing scope presented in the Navigation framework. It is then defined in the screens’ definition XML file but in the simplest form it is.
Document definition in the definition file :
<document name="treatment" action="edit">
</document>
Such a definition implies that it is possible to overwrite the process document screen in the standard and global way: contribution to the buttons (add/hide), on the header and history sections, and the display rules of cancellation and remind buttons.
In the same way as screens (form type), a process document handles a graphical representation template class, a document class and a provider.
Conceptual diagram
The following diagram displays the objects implemented in a process document:
Template class
The graphical representation template class used in the process documents is CtlAbstractDocument. Its full name is com.axemble.vdp.ui.framework.composites.base.CtlAbstractDocument
.
For any process document, this template class handles the following objects:
- Two form containers: the header (HEADER_DOCUMENT) and the document body composed of a process document (EDIT_DOCUMENT), and a step form in the case of intervention. These two containers use a unique CoreDocument object;
- Two button containers: the whole button put in the form top TOP_CONTAINER and the ones put in the form bottom BOTTOM_CONTAINER.
Document class
The document class used in the process documents is CoreDocument. Its full name is com.axemble.vdp.ui.core.document.CoreDocument
.
As the class AbstractDocument, this one remembers a fields list corresponding to the fields described in the administration forms. It is the Java representer of form data presented to the final user.
This class, dynamic and abstract notion representing the stored data in base, mainly enables to dynamically modify the process document fields values without forcing the physical storage in database. In fact, a series of actions can be run on the class without altering the data in any way.
It permits also to find dynamic information such as the current step or task, the connected user, the parent document (case of dynamic tables) and the back office objects too. However, these information can be accessed from the SDK API and the navigation.
Main methods of the CoreDocument class
Method | Description |
---|---|
public boolean isCreationMode() | This state lets you know whether the document being loaded will be created or not. |
public boolean isLoading() | This state indicates whether or not the document is being loaded. |
public boolean isModified | This state indicates whether the document was modified |
public IFieldControl getMainControlByPropertyName( String name ) | Finds the main graphic control (often in edit mode) corresponding to a given property. |
public ICoreField getFieldByName( String name ) | Finds the CoreField object through the property’s system name. |
public Collection getFields() | Finds all the CoreField objects active on the document. |
public void saveToResource | Saves the document. The data will be stored in the database. |
public CoreDocument getParentDocument | Finds the parent document in case of dynamic table of the “linked documents” type |
public ServiceManager getServiceManager | Finds the service object that allows access to all other managers and services. |
public User getUser() | Finds the connected user |
public Catalog getCatalog() | Finds the current catalog. |
public ResourceTemplate getResourceTemplate() | Finds the resource template associated with the document. |
public Treatment getTreatment() | Finds the back-office object representing the document in the database |
public Resource getResource() | Finds the back-office object representing all the document values. |
public ManualActivity getActivity() | Finds the active step of the document (access in intervention mode) |
public String getStageName() | Finds the system name of the document’s active step |
public String getStoragePath() | Returns the root directory of component descriptors and their associated graphic interface. |
Simplified diagram of the CoreDocument class
The following diagram displays the existing links between the couples (AbstractDocument, AbstractField) and (CoreDocument, CoreField).
This diagram identifies two document categories:
- CoreDocument : for process documents;
- GenericDocument : for every screen made for the administration, the application or about the document, such as the creation , the step change, the cancellation, the reminding, the deletion wizards, or even the information email sending.
Process fully use this document class to manage subscriptions between fields and every Java (extensions) or JavaScript code openings.
The objects linked to the CoreDocument class
The CoreDocument link is linked to fields composed of graphical components and a CoreField class that control the handled value.
The document fields implements the IFieldControl interface. For the ones which manage several values (ex.directory multiple selectors): they implement the IFieldListControl interface.
Principle schema between the linked objects
It exists an events wiring between the CoreField (the value) and the assigned graphical components. Indeed, if a field value is modified through the CoreDocument object, all the graphic components will be refreshed. On the other hand, if a user modifies the value of a field (from the interface), the latter will trigger an event that will update CoreField and then the other associated graphic components.
Events mechanism between the linked objects
This entire mechanism is managed in Process. It is applicable for fields subscriptions but also while running the extensions. This mechanism is put on the basic elements; AbstractDocument and AbstractField, and the GenericDocument class receives this event mechanism too.
CoreField class
The CoreField class is an abstract representation of the document’s field value. It implements the ICoreField interface. The aim of this class is to de-link the value of each document field from its graphical aspect. In fact, based on its design at the Web Designer level, a field can appear multiple times in the document (in multiple sections). The value can be represented differently (ex. in simple text form in a section in read mode, in the form of a list in the intervention section).
The ICoreField interface
public interface ICoreField {
public boolean loadFromResource();
public boolean loadFromAnotherDocument(CoreDocument srcDocument);
public boolean saveToResource();
public boolean isModified();
public Property getProperty();
public String getPropertyName();
public boolean setValue(Object value);
public boolean setValue(Object value, Object changeSource);
public Object getValue();
public boolean equalsValue(Object valueToCompare);
public CoreDocument getDocument();
public void release();
public void setList(Collection values, Object changeSource);
public Collection getList();
}
Provider class
The provider class associated by default to the process documents is: DefaultProcessDocumentProvider. Its full name is com.axemble.vdp.ui.core.providers.documents.DefaultProcessDocumentProvider
. You may define your own provider class thanks to the screens definition file (as the other screens).
Example of “process document” screen overridden
In this example, several standard settings are made:
- The screen named [treatment, edit] corresponding to the one of the process document is overridden.
- The document header is displayed and locked;
- The document history is hidden but can be activated with the History button;
- The display rules of cancellation, reminding, delegation buttons are locked and only the creator can activate the “Cancel my request” button, only the past contributors can activate the “Remind” button.
- Three buttons are added to the document. The first one is a navigation button to access the process management. The second one enables to display the help. It points at a local link on the server. The last one enables to display an external web page.
- The “Save” button is hidden for every document;
- At last, a process document provider is associated to the screen.
Example of a provider class associated to a process document
public class DemoProvider extends AbstractProcessDocumentProvider {
public DemoProvider(INavigateContext context,AbstractDocument document,CtlAbstractDocument
abstractDocument ) {
super( context, document, abstractDocument );
}
public boolean evaluateAbortRules() {
// if the value sent back is “true”, the cancellation button will display
return false;
}
public boolean evaluateDelegateRules() {
// if the value sent back is “true”, the delegation button will display
return false;
}
public boolean evaluateReminderRules() {
// if the value sent back is “true”, the remind button will display
return false;
}
public void readyState() {
super.readyState();
// here it is possible to add, hide buttons,
// assigning some document values.
}
}
Extensions on the document
Process lets the integrators develop Java classes that can react to event triggered by actions on the document (ex. Save, Close, Abort, etc).
This extension type must be used in the case when it is necessary to dynamically handle the document.
These extension classes are executed in the context of using forms. So, they may be defined in the following forms:
- of process;
- of step;
- of action;
- of table row.
It is also possible to put several extensions on a same form.
Process internally uses an extension class to manage the inter-field and inter-resource subscriptions at the same time. This class implements the IDocumentExtension3 and ILinkExtension interfaces.
Extension class lifecycle
Before the document is fully loaded, several methods are called:
isOnChangeSubscriptionOn()
:- this method is called on each field present in the form in “write” mode. It enables to define, for each one, if the server round trips on the field value change.
- Note: on the server side, the method
onPropertyChanged()
will be called in every case, if the field is modified. There is no direct link between the value sent back by the methodisOnChangeSubscriptionOn()
. By default, this method must send false back.
onBeforeLoad()
: corresponds to initialization of some fields (such as lists) without triggering an event. If you want to assign some document values without triggering any event, this is the ideal location. However, note that all the “onLoad” subscriptions are triggered before the method is called.onAfterLoad()
: at this step all subscriptions are read and are ready to triggered in case the field is modified (“onChange”).
On each field change, the onPropertyChanged()
method will be called in the following cases:
- if a subscription has been associated to it;
- if the true value has been sent back by theisOnChangeSubscriptionOn method ;
- if the « throw-events » attribute has been put as true.
The parameter put in the onPropertyChanged method corresponds to the property of the field: IProperty.
When saving, two events are processed:
onBeforeSave()
: this method is mainly present for value verification cases. If values are not desired, it is possible to abort the save process by returning the “false” value.onAfterSave()
: during step change of the document, it can be necessary to perform some verification operations before submitting the document. To do this, theonBeforeSubmit()
method lets you stop the step change process by returning “false”. TheonAfterSubmit()
method is only provided to indicate that the step change has occurred.onBeforeAbort()
: works in the same way as theonBeforeSubmit()
method. Starting the abort wizard will only be effective if “true” value is returned.
Creation of an extension class
Process provides a basic class which simplifies the extension classes implementation. This basic class has methods to directly access to the elements often used, such as the workflow module, the workflow instance and the resource controller.
To implement an extension class IDocumentExtension4 type, you just have to branch off the BaseDocumentExtension class. The extension class must be defined in the forms administration.
The full name is: com.axemble.vdoc.sdk.document.extensions.BaseDocumentExtension
.
Methods of the BaseDocumentExtension class
public abstract class BaseDocumentExtension implements IDocumentExtension4 {
// helper methods
public IResourceController getResourceController();
public IWorkflowInstance getWorkflowInstance();
public IWorkflowModule getWorkflowModule();
// load
public boolean onBeforeLoad();
public boolean onAfterLoad();
// subscription
public boolean isOnChangeSubscriptionOn( IProperty property );
public void onPropertyChanged( IProperty property );
// save
public boolean onBeforeSave();
public boolean onAfterSave();
// change stage
public boolean onBeforeSubmit( IAction action );
public boolean onAfterSubmit( IAction action );
// abort
public boolean onBeforeAbort();
// remind
public boolean onBeforeRemind();
// close
public boolean onBeforeClose();
// delegation
public boolean onBeforeDelegate();
public boolean onAfterDelegate();
public boolean onBeforeDelegateTaskOnly();
public boolean onAfterDelegateTaskOnly();
public boolean onBeforeRefuseDelegation();
public boolean onAfterRefuseDelegation();
public boolean onBeforeCancelDelegation();
public boolean onAfterCancelDelegation();
// send information
public boolean onBeforeSendInformation();
public boolean onAfterSendInformation();
}
Example of document extension implementation
As the following example shown, the class is much reduced. This example shows how to bring back the connected user, his superior, position the latter in a person selector field and add it the right to read on the current document.
public class GrantAccessToManager extends BaseDocumentExtension {
private static final long serialVersionUID = 3457373535964512940L;
public boolean onAfterLoad() {
try {
// get back the connected user
IUser user = this.getWorkflowModule().getLoggedOnUser();
// get back the connected user manager
IUser manager = user.getManager();
if ( manager == null ) {
Navigator.getNavigator().showAlertBox( "The connected user has no manager." );
return false;
}
// position of the value from a single user selector
this.getWorkflowModule().setExternalUser( this.getWorkflowInstance(), "VerificationChefDeService", manager );
// using the VDoc security manager to handle rights and add the right to read on the document
ISecurityController securityController = getWorkflowModule().getSecurityController( getWorkflowInstance() );
// position a reading right
securityController.addPermission( manager, Rights.Treatment.TreatmentLevel.READ_CONTENT );
} catch( WorkflowModuleException e ) {
Navigator.getNavigator().processErrors( e, true );
}
return super.onAfterLoad();
}
}
Programming tasks
Create a document
From the IWorkflow object, documents can be created. Please note that the third argument of the method createWorkflowInstance is null. This indicates that the system determines the document reference depending on the format specified in the web designer. However it is possible to force the reference to a precise value. In this case the third argument must be specified.
public void document_create( IContext context, IWorkflowModule workflowModule, IWorkflow workflow ) throws WorkflowModuleException {
try {
// starting a transaction
workflowModule.beginTransaction( this );
// creation of the document
IWorkflowInstance newInstance = workflowModule.createWorkflowInstance( context, workflow, null );
// positioning some values
newInstance.setValue( "fldDocumentVersion", "5.0" );
newInstance.setValue( "fldDocumentType", "Type simple" );
newInstance.setValue( "fldNature", "Interne" );
newInstance.setValue( "fldWritingRequired", "Non" );
newInstance.setValue( "fldReadingRequired", "Non" );
newInstance.setValue( "fldVerificationRequired", "Non" );
newInstance.setValue( "fldApprovingRequired", "Non" );
newInstance.setValue( "sys_Title", "Documentation SDK" );
newInstance.setValue( "fldDocumentDescription", "Les possibilités d'intégration" );
newInstance.setValue( "fldKeywords", "vdoc process sdk education" );
// backing-up modifications
newInstance.save();
// validating the transaction
workflowModule.commitTransaction( this );
} catch( Exception e ) {
// if an error has occurred, canceling the previous processing
workflowModule.rollbackTransaction( this );
getNavigator().processErrors( e );
}
}
Create a document and customizing the reference
The following example shows how to customize the reference before it is registered in database.
public void document_createWithCustomReference( IContext context, IWorkflowModule workflowModule, IWorkflow workflow, IResourceDefinition definition ) throws WorkflowModuleException {
try {
// starting a transaction
workflowModule.beginTransaction( this );
// 1st using form: retrieving the calculated reference for a
// resource definition
String defaultReference = workflowModule.generateReference( definition );
// 2nd using form: retrieving the calculated reference for a
// resource definition and a wanted format
String myReference = workflowModule.generateReference( definition, "[MYTAG]-[site]-[company]-[jj]-[mm]-[aaaa]-[aa]-[dd]-[yyyy]-[yy]-[chrono]" );
myReference = StringUtils.replaceAll( myReference, "[MYTAG]", "REF" );
// creation of the document
IWorkflowInstance newWorkflowInstance = workflowModule.createWorkflowInstance( context, workflow, myReference );
// positioning some values
// ...
// backing-up modifications
newWorkflowInstance.save();
// validating the transaction
workflowModule.commitTransaction( this );
} catch( Exception e ) {
// if an error has occurred, canceling the previous processing
workflowModule.rollbackTransaction( this );
getNavigator().processErrors( e );
} finally {
workflowModule.unInitialize();
}
}
Recover the next chrono digit of the formatted reference
The following example shows how to recover the next chrono digit of the formatted reference
public void document_getNextChrono( IWorkflowModule workflowModule, IResourceDefinition definition ) throws WorkflowModuleException {
// recovering the next chrono of the calculated reference
int nextChrono = workflowModule.generateChrono( definition );
}
Add a row in the history
The following example shows how to add an event in the document history.
public void document_addEventHistory( IContext context, IWorkflowModule workflowModule, IWorkflowInstance workflowInstance ) throws WorkflowModuleException {
IUser user = workflowModule.getUser( getNavigator().getLoggedOnUser() );
workflowInstance.getHistory().addEvent( "Action", "Etat", user, user, "Description", "En tant que" );
}
Add the right to read on the document
The following example shows how to add the right to read to a document.
public void document_addRights( IWorkflowModule workflowModule, IWorkflowInstance workflowInstance ) throws WorkflowModuleException {
IUser user = workflowModule.getUser( getNavigator().getLoggedOnUser() );
ISecurityController securityController = workflowModule.getSecurityController( workflowInstance );
securityController.addPermission( user, Rights.Treatment.TreatmentLevel.READ_CONTENT );
}
Position the value from a single user selector
The following example shows how to fill-in a single user selector. This example applies to every directory selectors (IUser, IGroup, ILocalization, IOrganization),as well as the publication module folder or file selector (IFile, IFolder).
public void document_setSelectorValue( IWorkflowModule workflowModule, IWorkflowInstance workflowInstance ) throws WorkflowModuleException {
// positioning the value from a single user selector
IUser user = workflowModule.getUserByLogin( "user1" );
workflowModule.setExternalUser( workflowInstance, "fldSingleUserSelector", user );
}
Position the value from a multiple users selector
The following example shows how to fill-in a multiple user selector. This example applies to every directory selectors (IUser, IGroup, ILocalization, IOrganization),as well as the publication module folder or file selector (IFile, IFolder).
public void document_setSelectorValues( IWorkflowModule workflowModule, IWorkflowInstance workflowInstance ) throws WorkflowModuleException {
// creation of a users list
ArrayList arrUsers = new ArrayList();
arrUsers.add( workflowModule.getUserByLogin( "user1" ) );
arrUsers.add( workflowModule.getUserByLogin( "user2" ) );
// positioning the value of a multiple users selector
workflowModule.setExternalElements( workflowInstance, "fldMultipleUserSelector", arrUsers );
}
Use a SQL request to recover documents
For an advanced using, VDoc provides a SQL request system that lets directly recover API SDK elements.
Every object implementing the interface ISearchSupport may be directly retrieved via a SQL request.
[com.axemble.vdoc.sdk.supports.ISearchSupport]
[IAction]
[ICatalog]
[IResource]
[ILinkedResource]
[IWorkflowInstance]
[IResourceDefinition]
[IRole]
[ITask]
[ITaskInstance]
[IUser]
[IOperator]
[IWorkflow]
[IWorkflowContainer]
Example1: recover every application documents
public void document_useSearch( IContext context, IWorkflowModule workflowModule ) {
try {
IJdbcReference jdbcReference = workflowModule.getJdbcReference( context, "vdocDatabase" );
ISearchController searchController = workflowModule.getSearchController( jdbcReference );
Collection<IWorkflowInstance> worflowInstances = (Collection<IWorkflowInstance>)searchController.findElements( IWorkflowInstance.class, "select id from vdp_treatments", null );
for ( IWorkflowInstance workflowInstance : worflowInstances ) {
// ...
}
} catch( Exception e ) {
getNavigator().processErrors( e );
}
}
Example2: recover the user whose login is “froggy”
Object[] arrParams = new Object[1];
arrParams[0] = "froggy";
Collection<IUser> users = (Collection<IUser>)searchController.findElements( IUser.class, "select id from vdp_users where login = ?", arrParams );
for ( IUser user : users ) {
// ...
}
Add attachments
In this example, three methods are displayed to add files in the attachments field.
public void document_createAttachment( IWorkflowModule module, IWorkflowInstance instance ) throws WorkflowModuleException {
// creating an attachment object from a file on the server
IAttachment idisk = module.addAttachment( instance, "propertyName", "xxx", "c:/xxx.xls" );
// creating an attachment object from a java.io.InputStream
InputStream inputStream = null;
IAttachment istream = module.addAttachment( instance, "propertyName", "document_name", inputStream );
// creating an attachment object from a java.io.File object
File file = new File( "c:/tmp/document_name.xls" );
IAttachment ifile = module.addAttachment( instance, "propertyName", file );
}
Recover files from an attachment field
The method getAttachments()
of the workflow module enables to recover every file of an attachment field. It is then possible, via the IAttachment
interface, to recover, for each file every information and content.
public void document_getAttachments( IWorkflowModule module, IWorkflowInstance instance ) throws WorkflowModuleException, FileNotFoundException {
Collection attachments = module.getAttachments( instance, "propertyName" );
for ( Iterator iter = attachments.iterator() ; iter.hasNext() ; ) {
IAttachment attachment = (IAttachment)iter.next();
// retrieving the name
attachment.getName();
// retrieving the size
attachment.getSize();
// retrieving the content in a OutputStream
attachment.getContent( new FileOutputStream( new File( "c:/tmp/xxx.yyy" ) ) );
// retrieving the content in a file
attachment.getContent( new File( "c:/tmp/xxx.yyy" ) );
}
}
Change Version Step
In this example, several elements are displayed:
- retrieving the context of the user “froggy” to evaluate if an active task exists on the document;
- retrieving the action from the action system name;
- using the workflow module to perform the step change.
public void document_changeStage( IWorkflowModule module, IWorkflowInstance instance ) throws WorkflowModuleException {
IContext localContext = module.getContextByLogin( "froggy" );
ITaskInstance taskInstance = instance.getCurrentTaskInstance( localContext );
if ( taskInstance != null ) {
IAction action = taskInstance.getTask().getAction( "actionName" );
if ( action != null ) {
module.end( localContext, taskInstance, action, "decription" );
}
}
}
Change a document in XML format
In this example are shown two methods for converting documents from and into XML.
public void document_transform( IWorkflowModule module, IWorkflowInstance instance ) throws WorkflowModuleException, IOException {
// retrieving a transformation object
ITransformer transformer = module.getTransformer();
// converting a workflow instance into an XML file
FileOutputStream xmlOutput = new FileOutputStream( new File( "D:/tmp/sdk/XXX.xml" ) );
transformer.resourceToXML( instance, xmlOutput );
// converting an XML file into a workflow instance
InputStream inputStream = new FileInputStream( new File( "D:/tmp/sdk/XXX.xml" ) );
transformer.xmlToResource( inputStream );
}
Lock a document
To allow disconnected mode on some processes, it is important to be able to add locks on some documents. Documents can be locked from the IWorkflowInstance object.
The following example shows how to lock a document using the BaseDocumentExtension.
import com.axemble.vdoc.sdk.document.extensions.BaseDocumentExtension;
import com.axemble.vdoc.sdk.interfaces.IContext;
import com.axemble.vdoc.sdk.interfaces.IResourceController;
import com.axemble.vdoc.sdk.interfaces.IWorkflowInstance;
import com.axemble.vdp.ui.framework.components.events.ActionEvent;
import com.axemble.vdp.ui.framework.components.listeners.ActionListener;
import com.axemble.vdp.ui.framework.runtime.Container;
import com.axemble.vdp.ui.framework.widgets.CtlButton;
import com.axemble.vdp.ui.framework.widgets.CtlText;
/**
* This class add a button that allows to lock a workflow document
*/
public class LockButtonExtension extends BaseDocumentExtension {
private static final long serialVersionUID = 1L;
public LockButtonExtension() {
}
@Override
public boolean onAfterLoad() {
IResourceController myResourceController = getResourceController();
Container myButtonContainer = myResourceController.getButtonContainer(IResourceController.BOTTOM_CONTAINER);
CtlButton button = new CtlButton("btLock", new CtlText("Lock the current document"));
button.addActionListener(new ActionListener() {
private static final long serialVersionUID = 3993412473482455550L;
@Override
public void onClick(ActionEvent event) {
IWorkflowInstance myWorkflowInstance = getWorkflowInstance();
IContext loggedOnUserContext = getWorkflowModule().getLoggedOnUserContext();
// Lock the workflowInstance for 3 minutes, if is not locked
if (!myWorkflowInstance.isLocked(getWorkflowModule().getSysadminContext()))
myWorkflowInstance.lock(loggedOnUserContext, 180);
}
});
myButtonContainer.add(button);
return super.onAfterLoad();
}
}
Unlock a document
A locked document can be unlocked from the IWorkflowInstance object.
The following example shows how to unlock documents in a workflow using an agent task.
import java.util.Collection;
import com.axemble.vdoc.sdk.Modules;
import com.axemble.vdoc.sdk.agent.base.BaseAgent;
import com.axemble.vdoc.sdk.exceptions.SDKException;
import com.axemble.vdoc.sdk.interfaces.ICatalog;
import com.axemble.vdoc.sdk.interfaces.IContext;
import com.axemble.vdoc.sdk.interfaces.IOrganization;
import com.axemble.vdoc.sdk.interfaces.IProject;
import com.axemble.vdoc.sdk.interfaces.IUser;
import com.axemble.vdoc.sdk.interfaces.IWorkflow;
import com.axemble.vdoc.sdk.interfaces.IWorkflowInstance;
import com.axemble.vdp.utils.CollectionUtils;
/**
* This agent unlocks all the locked documents in a workflow.
*/
public class UnLockProcessAgent extends BaseAgent {
final public static String PROJECT_NAME = "project_name";
final public static String WORKFLOW_NAME = "process_name";
final public static String CATALOG_NAME = "catalog_name";
final public static String SYSADMIN_LOGIN = "sysadmin";
final public static String USER_LOGIN = "user_login";
public UnLockProcessAgent() {
}
@Override
protected void execute() {
IContext sysadminContext = getWorkflowModule().getContextByLogin(SYSADMIN_LOGIN);
IUser user = getWorkflowModule().getContextByLogin(USER_LOGIN).getUser();
IContext sysAdminContext = getWorkflowModule().getSysadminContext();
IOrganization organization = user.getOrganization();
try {
IProject project = getProjectModule().getProject(sysadminContext, PROJECT_NAME, organization);
ICatalog catalog = getWorkflowModule().getCatalog(sysadminContext, CATALOG_NAME, project);
IWorkflow workflow = getWorkflowModule().getWorkflow(sysadminContext, catalog, WORKFLOW_NAME);
Collection<? extends IWorkflowInstance> myWorkflowinstances = getWorkflowModule().getWorkflowInstances(sysadminContext, workflow);
if (CollectionUtils.isNotEmpty(myWorkflowinstances)) {
for (IWorkflowInstance iWorkflowInstance : myWorkflowinstances) {
// Check if user is modifying this document
if (iWorkflowInstance.isLockBySDK(sysAdminContext)) {
continue;
}
Collection<IUser> userListCollection = iWorkflowInstance.lockedBy(sysadminContext);
for (IUser iUser : userListCollection) {
// Unlock the process document with the user who locked it
iWorkflowInstance.unlock(getWorkflowModule().getContext(iUser));
getReport().addInfo("The process document " + iWorkflowInstance.getValue("sys_Title") + " has been unlocked ");
}
}
}
} catch (Exception e) {
throw new SDKException(e);
} finally {
Modules.releaseModule(getProjectModule());
Modules.releaseModule(getWorkflowModule());
}
}
}