Skip to content

Commit

Permalink
impl, docu
Browse files Browse the repository at this point in the history
Issue #151
  • Loading branch information
rsoika committed Jan 2, 2024
1 parent 8b6911d commit 97ad0f9
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 76 deletions.
Binary file not shown.
Binary file not shown.
88 changes: 51 additions & 37 deletions imixs-adapters-wopi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ Add the following maven dependency into a parent project:

To open the LibreOffice Online Editor you need a access url including the Wopi Host Endpoint and the access token. The CDI Bean WopiController provides a convenient method to generate such a URL:


<a href="javascript:void;"
onclick="imixsWopi.openViewer('wopi_canvas','#{wopiController.getWopiAccessURL(uniqueID,filename,userid,username)}');">
Edit</a>
```html
<a href="javascript:void;"
onclick="imixsWopi.openViewer('wopi_canvas','#{wopiController.getWopiAccessURL(uniqueID,filename,userid,username)}');">
Edit</a>
```

In this example we are calling the JavaScript method to open the viewer component in a iframe. See details in the following section.

Expand All @@ -128,28 +129,30 @@ The Integration of the Wopi Client into your application is done by a iframe. Th
To display the editor in a iframe the script library *imixs-wopi.js* provides a method `imixsWopi.openViewer`. The method expects a DIV element in your existing web page to place the iframe with the editor and the access URL to load the document.


<script type="text/javascript" src="/js/imixs-core.js"></script>
<script type="text/javascript" src="/js/imixs-wopi.js"></script>
<script>
// open the wopi viewer
function openWopiViewer(url) {
// open viewer...
imixsWopi.openViewer('wopi_canvas', url);
// define an optional save callback method
//imixsWopi.saveCallback=closeWopiViewer;
}
</script>


....
...........
<!-- Office Editor -->
<div id="wopi_controlls">
<button onclick="imixsWopi.save(); return false;">Update</button><button onclick="imixsWopi.closeViewer(); return false;">Close</button>
<hr />
</div>
<div id="wopi_canvas" style="display: none;"></div>
....
```html
<script type="text/javascript" src="/js/imixs-core.js"></script>
<script type="text/javascript" src="/js/imixs-wopi.js"></script>
<script>
// open the wopi viewer
function openWopiViewer(url) {
// open viewer...
imixsWopi.openViewer('wopi_canvas', url);
// define an optional save callback method
//imixsWopi.saveCallback=closeWopiViewer;
}
</script>


....
...........
<!-- Office Editor -->
<div id="wopi_controlls">
<button onclick="imixsWopi.save(); return false;">Update</button><button onclick="imixsWopi.closeViewer(); return false;">Close</button>
<hr />
</div>
<div id="wopi_canvas" style="display: none;"></div>
....
```

## UI Controls

Expand All @@ -159,6 +162,7 @@ The control of closing the editor or saving the content in this concept is part

When a file was saved by the Office interface, the data is posted to the WOPI Host endpoint '/wopi/files/{name}/contents'. The file content is not directly stored. It is cached into the local wopi file cache on the Wopi Host. An application can provide a saveCallback method to be triggered after a file was updated.

```javascript
// define save callback when a file was updated....
imixsWopi.saveCallback = uiSaveCallback;

Expand All @@ -181,15 +185,15 @@ When a file was saved by the Office interface, the data is posted to the WOPI Ho
// show workflow form
$('#imixs_workitem_form_id').show();
}

```


## Reacting on PostMessage Events

LibreOffice Online sends JavaScript general events each time an update of the content is performed by the user.
A javaScript can react on these events be registering a EventListner:


```javascript
/**
* Register a message listener
*/
Expand All @@ -204,9 +208,11 @@ A javaScript can react on these events be registering a EventListner:
console.log('==== framed.doc.html receiveMessage: ' + event.data);
... do something....
}
```

You can also send messages to the editor

```javascript
imixsWopi.postMessage({
"MessageId" : "Action_Save",
"Values" : {
Expand All @@ -215,6 +221,7 @@ You can also send messages to the editor
"Notify" : true
}
});
```

Find more details about the Post Message in Collaboara [here](https://sdk.collaboraonline.com/docs/postmessage_api.html).

Expand Down Expand Up @@ -283,9 +290,6 @@ The WopiController will automatically clean the flag before processing.
In a Kubernetes environment the office templates can be provided in a ConfigMap object



## The Wopi Document Converter Adapter

With the adapter class *org.imixs.workflow.wopi.WopiDocumentConverterAdapter* a office document can be converted into PDF or other file formats.
Expand All @@ -305,18 +309,28 @@ The rest service automatically detects the input document format. You can test t

The adapter simply posts a given document to the service endpoint. The adapter can be configured by the BPMN event workflow result:

<wopi-converter name="api-endpoint">http://localhost:9980/lool/convert-to/</wopi-converter>
<wopi-converter name="filename">......</wopi-converter>
<wopi-converter name="convert-to">pdf</wopi-converter>
```xml
<wopi-converter>
<api-endpoint>http://localhost:9980/lool/convert-to/</api-endpoint>
<filename>......</filename>
<convert-to>pdf</convert-to>
</wopi-converter>
```

The Collabora API endpoint must point to a collabora instance. The 'filename' is the file attached to the current workitem. The option 'convert-to' is optional and default value is 'pdf'
The Collabora API endpoint must point to a collabora instance. The 'filename' is the file attached to the current workitem. The option 'convert-to' is optional and default value is 'pdf'. You can use multiple wopi-converter configurations in one event.


### Regular Expressions

You can also define the filename as a pattern including regulare expressins. See the following example:
You can also define the filename as a pattern including regulare expressions. See the following example:

<wopi-converter name="filename">.*<itemvalue>numsequencenumber</itemvalue>\.docx</wopi-converter>
```xml
<wopi-converter>
...
<filename>.*<itemvalue>numsequencenumber</itemvalue>\.docx</filename>
...
</wopi-converter>
```

This expression will match all files ending with the sequence number and the file extension '.docx'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Pattern;
Expand All @@ -21,6 +22,8 @@
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.AdapterException;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.ProcessingErrorException;
import org.imixs.workflow.util.XMLParser;

import jakarta.inject.Inject;

Expand Down Expand Up @@ -87,52 +90,77 @@ public class WopiDocumentConverterAdapter implements SignalAdapter {
public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException, PluginException {

ItemCollection wopiConverterConfig = workflowService.evalWorkflowResult(event, "wopi-converter", document,
true);
if (wopiConverterConfig == null || !wopiConverterConfig.hasItem("api-endpoint")) {
throw new PluginException(WopiDocumentConverterAdapter.class.getSimpleName(), CONFIG_ERROR,
"Converter Error: 'api-endpoint' is not defined in current BPMN configuration");
ItemCollection evalItemCollection = null;
List<ItemCollection> wopiConverterConfigList = null;

// test for deprecated configuration using the <item> tag....
if (isDeprecatedConfiguration(document, event)) {
logger.warning(
"WopiTemplateAdapter is using deprecated configuration! Please use <wopi-template> instead of <wopi-converter name='source-path'> - see documentation for details!");
evalItemCollection = workflowService.evalWorkflowResult(event, "wopi-converter", document,
true);
wopiConverterConfigList = new ArrayList<>();
wopiConverterConfigList.add(evalItemCollection);
} else {
String workflowResult = event.getItemValueString("txtActivityResult");
wopiConverterConfigList = XMLParser.parseTagList(workflowResult, "wopi-converter");
}
if (wopiConverterConfig == null || !wopiConverterConfig.hasItem("filename")) {
throw new PluginException(WopiDocumentConverterAdapter.class.getSimpleName(), CONFIG_ERROR,
"Converter Error: 'filename' is not defined in current BPMN configuration");

if (wopiConverterConfigList == null || wopiConverterConfigList.size() == 0) {
throw new ProcessingErrorException(WopiTemplateAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing wopi-converter configuraiton in BPMN event!");
}

String fileName = wopiConverterConfig.getItemValueString("filename");
fileName = workflowService.adaptText(fileName, document);
// iterate over all wopi-template configurations
for (ItemCollection wopiConverterConfig : wopiConverterConfigList) {

String apiEndpoint = wopiConverterConfig.getItemValueString("api-endpoint");
if (!apiEndpoint.endsWith("/")) {
apiEndpoint = apiEndpoint + "/";
}
String convertTo = wopiConverterConfig.getItemValueString("convert-to");
if (convertTo.isEmpty()) {
convertTo = "pdf";
}
String uri = apiEndpoint + convertTo;
if (!wopiConverterConfig.hasItem("api-endpoint")) {
throw new PluginException(WopiDocumentConverterAdapter.class.getSimpleName(), CONFIG_ERROR,
"Wopi-Converter Error: 'api-endpoint' is not defined in current BPMN configuration");
}
if (!wopiConverterConfig.hasItem("filename")) {
throw new PluginException(WopiDocumentConverterAdapter.class.getSimpleName(), CONFIG_ERROR,
"Wopi-Converter Error: 'filename' is not defined in current BPMN configuration");
}

logger.info("WopiDocumentConverter: " + fileName + " => " + uri);
String apiEndpoint = wopiConverterConfig.getItemValueString("api-endpoint");
String fileName = wopiConverterConfig.getItemValueString("filename");
String convertTo = wopiConverterConfig.getItemValueString("convert-to");

// test all file matching the filename or regular expression
FileData fileData = document.getFileData(fileName);
if (fileData != null) {
// file data found by name directly - so we can convert it....
convertFile(fileData, document, uri);
} else {
// not found, we can test regular expressions...
List<String> fileNames = document.getFileNames();
Pattern pattern = Pattern.compile(fileName);
// get all fileNames....
for (String aFileName : fileNames) {
// test if aFilename matches the pattern or the pattern is null
if (pattern.matcher(aFileName).find()) {
// fetch the file
fileData = document.getFileData(aFileName);
if (fileData != null) {
// file data found - so we can updated it....
convertFile(fileData, document, uri);
}
fileName = workflowService.adaptText(fileName, document);

if (!apiEndpoint.endsWith("/")) {
apiEndpoint = apiEndpoint + "/";
}

if (convertTo.isEmpty()) {
convertTo = "pdf";
}
String uri = apiEndpoint + convertTo;

logger.info("WopiDocumentConverter: " + fileName + " => " + uri);

// test all file matching the filename or regular expression
FileData fileData = document.getFileData(fileName);
if (fileData != null) {
// file data found by name directly - so we can convert it....
convertFile(fileData, document, uri);
} else {
// not found, we can test regular expressions...
List<String> fileNames = document.getFileNames();
Pattern pattern = Pattern.compile(fileName);
// get all fileNames....
for (String aFileName : fileNames) {
// test if aFilename matches the pattern or the pattern is null
if (pattern.matcher(aFileName).find()) {
// fetch the file
fileData = document.getFileData(aFileName);
if (fileData != null) {
// file data found - so we can updated it....
convertFile(fileData, document, uri);
}

}
}
}
}
Expand Down Expand Up @@ -267,4 +295,26 @@ private static byte[] readAllBytes(InputStream inputStream) throws IOException {
}
}

/**
* This method tests if the BPMN configuration is still using the deprecated tag
*
* <wopi-template name="source-path">....
*
* instead of the new
*
* <wopi-template>...
*
* @param event
* @return
* @throws PluginException
*/
private boolean isDeprecatedConfiguration(ItemCollection workitem, ItemCollection event) throws PluginException {

String workflowResult = event.getItemValueString("txtActivityResult");
if (!workflowResult.contains("<wopi-converter>")) {
return true;
}

return false;
}
}

0 comments on commit 97ad0f9

Please sign in to comment.