Creating your own FormInterceptor plugin

Available since OpenKM version 8.1.15

You can create your own form interceptor plugin to intercept all the add, set and get methods.

Description of common features allowed to be implemented by this plugin type:

  1. Intercept the values of the properties before creation (add) what allows to:
    1. Update values
    2. Validate values
    3. Autocomplete values
  2. Intercept get properties or form elements to:
    1. Remove values ( fields )
    2. Change UI behaviour of form elements ( set fields as read-only, hide fields, etc )
    3. Create form elements on the fly what will not be keep in the database ( requires filtering in add and set stages )
    4. Apply mask to values to be displayed ( protect critical values )
    5. Change the order of the form elements
  3. intercept the values of the properties before update (set) to:
    1. Update values
    2. Validate values
    3. Autocomplete values

Conditions:

  • The new Form Validator class must implement the "FormInterceptor" interface.
  • The new Form Validator class must be declared under the package "com.openkm.plugin.form.interceptor".
  • The new Form Validator class must be annotated with "@PluginImplementation".
  • The new Form Validator class must extend of "BasePlugin".

Form Validator interface:

package com.openkm.plugin.form.interceptor;

import com.openkm.bean.form.FormElement;
import com.openkm.core.ValidationFormException;
import net.xeoh.plugins.base.Plugin;

import java.util.List;
import java.util.Map;


public interface FormInterceptor extends Plugin {
	String getName();

	void add(String uuid, String grpName, Map<String, String> properties) throws ValidationFormException;

	void get(String uuid, String grpName, Map<String, String> properties, List<FormElement> formElements, boolean useDefaultValue, boolean autocomplete);

	void set(String uuid, String grpName, Map<String, String> properties) throws ValidationFormException;
}

The new class must be loaded into the package com.openkm.plugin.form.interceptor because the application plugins system will try to load from there.

Do not miss the tag @PluginImplementation; otherwise, the application plugin system cannot retrieve the new class.

More information at Register a new plugin.

Methods description

MethodTypeDescription

getName()

String

The method returns the name of the plugin

add(String uuid, String grpName, Map<String, String> properties)

void

Intercept the action of adding metadata.

get(String uuid, String grpName, Map<String, String> properties, List<FormElement> formElements, boolean useDefaultValue, boolean autocomplete)

void

Intercept the action of getting metadata.

set(String uuid, String grpName, Map<String, String> properties)

void

Intercept the action of set metadata.

Example of the Form validator implementation

Metadata definition:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE property-groups PUBLIC "-//OpenKM//DTD Property Groups 3.10//EN"
                                 "http://www.openkm.com/dtd/property-groups-3.10.dtd">
<property-groups>
  <property-group label="Consulting" name="okg:consulting">
    <input label="Input label" name="okp:consulting.input1" />
    <separator label="Separator label" name="okp:consulting.separator" /> 
    <input label="Input label" name="okp:consulting.input2" />
  </property-group>
</property-groups>

In the following example plugin, different modifications are applied to the metadata only during the add, get, and set actions for a user with the ID "test." We suggest testing the example with this user and a different one.

package com.openkm.plugin.form.interceptor;

import com.openkm.bean.form.FormElement;
import com.openkm.bean.form.Input;
import com.openkm.bean.form.ValidationError;
import com.openkm.core.DatabaseException;
import com.openkm.core.PathNotFoundException;
import com.openkm.core.ValidationFormException;
import com.openkm.db.service.NodeBasePropertiesSrv;
import com.openkm.plugin.BasePlugin;
import com.openkm.principal.PrincipalUtils;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@PluginImplementation
public class SampleFormInterceptor extends BasePlugin implements FormInterceptor {
	private static final Logger log = LoggerFactory.getLogger(SampleFormInterceptor.class);
	private final String TEST_USER = "test";

	@Autowired
	private NodeBasePropertiesSrv nodeBasePropertiesSrv;

	@Override
	public String getName() {
		return "SampleFormInterceptor";
	}

	/**
	 * Description of common features allowed to be implemented by this plugin type:
	 * 1. intercept the values of the properties before creation (add) what allows to:
	 * - update values
	 * - validate values
	 * - autocomplete values
	 * <p>
	 * 2. Intercept get properties or form elements to:
	 * - remove values ( fields )
	 * - change UI behaviour of form elements ( set fields as read-only, hide fields, etc )
	 * - create form elements on the fly what will not be keep in the database ( requires filtering in add and set stages )
	 * - apply mask to values to be displayed ( protect critical values )
	 * - change the order of the form elements
	 * <p>
	 * 3. intercept the values of the properties before update (set) to:
	 * - update values
	 * - validate values
	 * - autocomplete values
	 **/
	@Override
	public void add(String uuid, String grpName, Map<String, String> properties) throws ValidationFormException {
		log.info("add({}, {}, {})", uuid, grpName, properties);
		String userId = PrincipalUtils.getUser();

		if (TEST_USER.equals(userId)) {
			// Iterate through the properties map and log each property individually
			log.info("Properties in group '{}' for node '{}':", grpName, uuid);

			for (Map.Entry<String, String> entry : properties.entrySet()) {
				log.info("  Property - Key: '{}', Value: '{}'", entry.getKey(), entry.getValue());

				// update the value of the property with the key "okp:consulting.input1"
				if ("okp:consulting.input1".equals(entry.getKey())) {
					properties.put(entry.getKey(), entry.getValue() + " ( Updated by SampleFormInterceptor at add )");
				} else if ("okp:consulting.input2".equals(entry.getKey())) {
					String valueInput2 = entry.getValue();

					if (StringUtils.isEmpty(valueInput2) || valueInput2.length() < 4) {
						// Show error in UI
						Map<String, List<ValidationError>> errors = new HashMap<>();
						List<ValidationError> validationErrors = new ArrayList<>();
						ValidationError validationError = new ValidationError();
						validationError.setErrorMessage("Field input2 is mandatory and must have at least 4 characters");
						validationError.setPropertyName("okp:consulting.input2");
						validationError.setErrorCode("ERROR-003345");
						validationError.setPropertyLabel("Input label 2");
						validationError.setUuid(uuid);
						validationErrors.add(validationError);
						errors.put("Validation was not successfully", validationErrors);
						throw new ValidationFormException(errors);
					}
				}
			}
		}
	}

	@Override
	public void get(String uuid, String grpName, Map<String, String> properties, List<FormElement> formElements, boolean useDefaultValue,
					boolean autocomplete) {
		// Use default value is true when showing form elements before adding the metadata group
		String userId = PrincipalUtils.getUser();

		if (TEST_USER.equals(userId)) {
			if (properties != null) {
				properties.remove("okp:consulting.input1");
				properties.put("okp:consulting.input3", "On the fly field");
			}

			if (CollectionUtils.isNotEmpty(formElements)) {
				formElements.removeIf(formElement -> "okp:consulting.input1".equals(formElement.getName()));

				// Add form element on the fly
				Input input = new Input();
				input.setName("okp:consulting.input3");
				input.setLabel("Input label 3");
				input.setType("text");
				input.setValue("On the fly field");
				input.setReadonly(true);
				formElements.add(input);
			}
		}
	}

	@Override
	public void set(String uuid, String grpName, Map<String, String> properties) throws ValidationFormException {
		log.info("set({}, {}, {})", uuid, grpName, properties);
		String userId = PrincipalUtils.getUser();

		if (TEST_USER.equals(userId)) {
			// Because the parameter "okp:consulting.input1" is not in the properties map, it will be added to the map
			// to keep the properties map consistent with the form elements in the database
			Map<String, String> currentProperties;

			try {
				currentProperties = nodeBasePropertiesSrv.getProperties(uuid, grpName, true);
			} catch (PathNotFoundException | DatabaseException e) {
				throw new RuntimeException(e);
			}

			if (currentProperties.containsKey("okp:consulting.input1")) {
				properties.put("okp:consulting.input1", properties.get("okp:consulting.input1"));
			}

			if (properties.containsKey("okp:consulting.input2")) {
				properties.put("okp:consulting.input2", properties.get("okp:consulting.input2"));
			}

			if (properties.containsKey("okp:consulting.input2")) {
				String valueInput2 = properties.get("okp:consulting.input2");

				if (StringUtils.isEmpty(valueInput2) || valueInput2.length() < 4) {
					// Show error in UI
					Map<String, List<ValidationError>> errors = new HashMap<>();
					List<ValidationError> validationErrors = new ArrayList<>();
					ValidationError validationError = new ValidationError();
					validationError.setErrorMessage("Field input2 is mandatory and must have at least 4 characters");
					validationError.setPropertyName("okp:consulting.input2");
					validationError.setErrorCode("ERROR-003345");
					validationError.setPropertyLabel("Input label 2");
					validationError.setUuid(uuid);
					validationErrors.add(validationError);
					errors.put("Validation was not successfully", validationErrors);
					throw new ValidationFormException(errors);
				}
			}
		}
	}
}