Skip to content

Commit

Permalink
impl, docu
Browse files Browse the repository at this point in the history
Issue imixs#222
  • Loading branch information
rsoika committed Mar 21, 2023
1 parent 131d46f commit 6e93d25
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 76 deletions.
48 changes: 44 additions & 4 deletions doc/BPMN_PROPERTIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,9 @@ In some cases the `BPMNApplyPropertiesUpdateOperation` causes on the server side

In this cases the server sends a `BPMNPropertyPanelUpdateAction` to the client. The BPMN Property Panel reacts on this kind of action and updates the panel content.

# Custom Renderer (Imixs-Workflow)
# Custom Renderer SelectItemControl

Most of the data to be displayed can be handled by JsonForms out of the box, so we only need to provide the corresponding schemata. But in some cases - e.g. the Imixs-Workflow extension - the corresponding input form is more complex. For this we implement a [custom renderer](https://jsonforms.io/docs/tutorial/custom-renderers) to provide an optimized input form.

## SelectItemControl
Most of the data to be displayed can be handled by JsonForms out of the box, so we only need to provide the corresponding schemata. But in some cases - e.g. the SignalEvent option or the Imixs-Workflow extensions - the corresponding input form is more complex. For this we implement a [custom renderer](https://jsonforms.io/docs/tutorial/custom-renderers) to provide an optimized input form.

The `SelectItemControl` is a custom renderer for selectItems represented as RadioButtons, CheckBoxes or ComboBoxes.
The control can handle single String values (represented as a Radio Button or ComboBox) or Arrays of Strings (represented as Checkboxes).
Expand All @@ -73,3 +71,45 @@ In addition the renderer support label|value pairs separated by a | char.
This allows to separate the label and data value in one string.

In addition the layout for Checkboxes and RadioButtons can be customized by the option `orientation=horizontal|vertical`.

<img src="./images/property-panel-selectitem.png" />

Even the user can select the option by the name, internally the value will be mapped to the id.
## UI Schema
To set the UISchema using a radio button:

```java
Map<String, String> selectVertical = new HashMap<>();
selectVertical.put("format", "selectitem");
selectVertical.put("orientation", "vertical");
uiSchemaBuilder //
.addElement("keyownershipfields", "Owner", selectVertical)
```

To set the UISchema using a comboBox:

```java
Map<String, String> selectVertical = new HashMap<>();
selectVertical.put("format", "selectitemcombo");
uiSchemaBuilder //
.addElement("keyownershipfields", "Owner", selectVertical)
```

## Schema

The schema defines the option list as `label | value` pairs

```java
String[] enabledOption = { "Yes|true", "No|false" };
schemaBuilder.addProperty("myitem", "string", "", enabledOption);
```

## Data

The data can be either a single string displayed as a radio-button or comboBox. Or a String Array displayed as check-boxes.

```java
dataBuilder //
.addData("keyupdateacl",mystringValue) //
.addDataList("keyownershipfields",myValueList)) //
```
Binary file added doc/images/property-panel-selectitem.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import java.util.Map;
import java.util.Set;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -30,9 +33,6 @@
import org.openbpmn.bpmn.elements.Event;
import org.openbpmn.bpmn.elements.Signal;
import org.openbpmn.bpmn.elements.core.BPMNElement;
import org.openbpmn.bpmn.exceptions.BPMNInvalidReferenceException;
import org.openbpmn.bpmn.exceptions.BPMNInvalidTypeException;
import org.openbpmn.bpmn.exceptions.BPMNMissingElementException;
import org.openbpmn.glsp.jsonforms.DataBuilder;
import org.openbpmn.glsp.jsonforms.SchemaBuilder;
import org.openbpmn.glsp.jsonforms.UISchemaBuilder;
Expand Down Expand Up @@ -76,48 +76,22 @@ public boolean handlesElementTypeId(final String elementTypeId) {
* Adds the SignalEvent definitions
* <p>
* Note: Internally we need a mapping between the Signal name (Label) and the
* Signal id (value). As
* discussed here
* (https://jsonforms.discourse.group/t/how-to-separate-value-and-label-in-a-combobox/1200)
* we do not have this feature yet. Currently the efforts seems to be to high to
* implement a new
* renderer for JsonForms.
* Signal id (value). This can be done with the Imixs selectitem renderer using
* using label|value pairs.
*/
@Override
public void buildPropertiesForm(final BPMNElement bpmnElement, final DataBuilder dataBuilder,
final SchemaBuilder schemaBuilder, final UISchemaBuilder uiSchemaBuilder) {

Event event = (Event) bpmnElement;

// Conditional

Set<Element> signalEventDefinitions = event.getEventDefinitionsByType("signalEventDefinition");

if (signalEventDefinitions.size() > 0) {
Map<String, String> arrayDetailOption = new HashMap<>();
// GENERATED HorizontalLayout
arrayDetailOption.put("detail", "GENERATED");

uiSchemaBuilder. //
addCategory("Signals"). //
addLayout(Layout.VERTICAL);
uiSchemaBuilder.addElement("signals", "Signals", arrayDetailOption);
// uiSchemaBuilder.addElement("formalExpression", "Script", multilineOption);

schemaBuilder.addArray("signals");

// find all signals in the current model and build an array...
Set<Signal> bpmnSignals = modelState.getBpmnModel().getSignals();
String[] signalOptions = new String[bpmnSignals.size()];
int i = 0;
for (Signal bpmnSignal : bpmnSignals) {
signalOptions[i] = bpmnSignal.getName();
i++;
// signalOptions[i] = bpmnSignal.getId() + "|" + bpmnSignal.getName();
}
schemaBuilder.addProperty("signal", "string", null, signalOptions);

/*
/***********
* Data
*
* Now we can create the data structure - each signalEventDefinition is
* represented as a separate object. We resolve the signalRef
*/
Expand All @@ -126,27 +100,59 @@ public void buildPropertiesForm(final BPMNElement bpmnElement, final DataBuilder
dataBuilder.addObject();
String signalRefID = definition.getAttribute("signalRef");
if (!signalRefID.isEmpty()) {
// fetch the corresponding Signal
Signal bpmnSignal = (Signal) modelState.getBpmnModel().findElementById(signalRefID);
if (bpmnSignal != null) {
dataBuilder.addData("signal", bpmnSignal.getName());
} else {
logger.warn("invalid signalRefID found: " + signalRefID);
}
dataBuilder.addData("signal", signalRefID);
}
}

/***********
* Schema
*
* providing the label|value paris for each signal defined in the bpmn
* definitions
*/
schemaBuilder.addArray("signals");
Set<Signal> bpmnSignals = modelState.getBpmnModel().getSignals();
String[] signalOptions = new String[bpmnSignals.size()];
int i = 0;
for (Signal bpmnSignal : bpmnSignals) {
// signalOptions[i] = bpmnSignal.getName();
signalOptions[i] = bpmnSignal.getName() + "|" + bpmnSignal.getId();
i++;
}
schemaBuilder.addProperty("signal", "string", "", signalOptions);

/***********
* UISchema
*
* for signal we use the custom renderer for imixs-selectitems
*/
JsonObject comboBoxOption = Json.createObjectBuilder() //
.add("format", "selectitemcombo").build();
uiSchemaBuilder. //
addCategory("Signals"). //
addLayout(Layout.VERTICAL);
// create a detail control Layout....
JsonArrayBuilder controlsArrayBuilder = Json.createArrayBuilder();
controlsArrayBuilder //
.add(Json.createObjectBuilder() //
.add("type", "Control") //
.add("scope", "#/properties/signal")//
.add("options", comboBoxOption) //
);
JsonObjectBuilder detailLayoutBuilder = Json.createObjectBuilder(). //
add("type", "VerticalLayout"). ///
add("elements", controlsArrayBuilder);
JsonObjectBuilder detailBuilder = Json.createObjectBuilder(). //
add("detail", detailLayoutBuilder.build());
uiSchemaBuilder.addDetailLayout("signals", "Signals", detailBuilder.build());

}

}

/**
* This method updates the signalEventDefinitions. The method expects a
* dataList containing all conditions with its values (including the id).
* The method simply overwrites all signalEventDefinitions.
*
* @See addSignalEventDefinitions how we map between the signal name and its id.
* This method updates the signalEventDefinitions. With the help of
* the imixs selectItem holding label|value pairs we can update the id directly.
*
*/
@Override
Expand All @@ -160,39 +166,16 @@ public void updatePropertiesData(final JsonObject json, final String category, f

Event bpmnEvent = (Event) bpmnElement;
JsonArray dataList = json.getJsonArray("signals");

// synchronize the definition list of the event element
Set<Element> signalEventDefinitions = synchronizeEventDefinitions("signalEventDefinition", bpmnEvent, dataList);

// now we can update the values one by referring to the signalRef id by
// comparing the name
// now we can update the values
Iterator<Element> iter = signalEventDefinitions.iterator();
int i = 0;
while (iter.hasNext()) {
Element eventDefinitionElement = iter.next();
JsonObject jsonData = dataList.getJsonObject(i); // .get(i);
JsonObject jsonData = dataList.getJsonObject(i);
if (jsonData != null) {

String signalName = "";

try {
signalName = jsonData.getString("signal");
} catch (NullPointerException en) {
// no name defined!
}
logger.debug("signal=" + signalName);
try {
// fetch the signal from teh Model Signal list by name...
Signal signal = modelState.getBpmnModel().findSignalByName(signalName);
if (signal != null) {
eventDefinitionElement.setAttribute("signalRef", signal.getId());
} else {
// no signal definition found - delete signalRef...
eventDefinitionElement.setAttribute("signalRef", "");
}
} catch (BPMNInvalidReferenceException | BPMNMissingElementException | BPMNInvalidTypeException e) {
e.printStackTrace();
}
eventDefinitionElement.setAttribute("signalRef", jsonData.getString("signal", ""));
}
i++;
// update completed
Expand Down

0 comments on commit 6e93d25

Please sign in to comment.