Create your own import station plugin

To create your own import plugin must create a new class that implements PropertiesProcessor interface:

public interface PropertiesProcessor extends Plugin {
    String getName();
    String getDescription();
    Class<?> getFormBeanClass();
    boolean doJob(long executionTaskId, FormProperties formProperties, OKMWebservices webService);
    String getAddTaskFormName();
    String getEditTaskFormName();
}

Do not miss the tag @PluginImplementation otherwise the application plugin system will not be able to retrieve the new class.

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

Methods description

MethodReturnDescription
getName() String

This method will provide the name of the plugin.

getDescription() String

Detailed plugin description shown below the combo named "Processor" in the task edit view.

getFormBeanClass() Class<?>

Return the bean used to store the information of the application form.

doJob(long executionTaskId, FormProperties formProperties, WsInterface wsInterface) boolean

This is the main method of the plugin.

getAddTaskFormName() String

Return the name of the form used for creating properties.

getEditTaskFormName() String

Return the name of the form used for editing properties.

Form bean

The plugin must have a bean class associated what is used for setting plugin custom properties. These properties will be stored in a map named customProperties.

The Form bean class must extend FormProperties class.

You can get these properties values from object formProperties in the method doJob. The  object formProperties has a field named customProperties.

Restrictions:

  • The value of the keys into the map, must be the same of the HTML form names.

Sample:

For example if we want to store a String property named path in this form, the html form name must be <input name="path" ...>. 

Html forms

The plugin must have two forms:

  • Create task form.
  • Edit task form.

They must be located inside src/main/html folder.

We need these forms, because each plugin will have it's own custom properties.

The name convention should be: <name_of_plugin>-form.html and <name_of_plugin>-form-edit.html but could be whatever name.

This convention is usefull in order to not override predefined forms. 

Sample:

  • Plugin class at src/main/java/com/openkm/processor/ExampleProcessor.class
  • Create form at src/main/html/ExampleProcessor-form.html
  • Edit form  at src/main/html/ExampleProcessor-form.html

These forms will be loaded in the Add Task and Edit Task web screens when processor combo is changed.

They should contain neccessary HTML and Javascript code in order to store and load Processor properties.

In edit form, for automatic filling fields you must use the class="form-control parameter".

For example:

<input type="text" class="form-control parameter" name="userPath">

Will set the parameter userPath and into the input with name equals to "userPath".

Event log

Application have a log feature what enables to get a complete trace of what is happening while processing a task.

Log or not the events is an implementation decision of the plugin code designer. We encourage log every event, however is a decision of the plugin code designer do it or not.

For this purpose the class used to log events is ImportStationLogger.

There are the following methods available:

MethodReturnDescription
logOk(long taskExecutionId, String description) void

Log and event to indicate that the operation was OK

logErrorEvent(long taskExecutionId, String description) void

Log and event to indicate that there was an error.

logErrorEvent(long taskExecutionId, String header, Exception e)

void

Log and event to indicate that there was an error. This method will log exception stack trace as description.

The parameter header is used to add a log message to the exception stack trace.

Example:

long taskExecutionId = ...;
// Mark as ok
ImportStationLogger.logOkEvent(taskExecutionId, "description");
// Mark with errors
ImportStationLogger.logErrorEvent(taskExecutionId, "description of error");
// Mark with error and exception detail
ImportStationLogger.logErrorEvent(taskExecutionId, "header", new Exception("exception"));

Example

This is a sample pom.xml configuration:

You can download source code from here: import-station-example-plugin.zip 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>copm</groupId>
	<artifactId>com</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>example</name>
	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<openkm-sdk4j.version>2.4</openkm-sdk4j.version>
		<jspf.version>1.0.3.1</jspf.version>
		<openkm-sdk4j.version>2.4</openkm-sdk4j.version>		
		<openkm-import-station-core.version>1.0</openkm-import-station-core.version>		
	</properties>
	<repositories>
		<repository>
			<id>openkm.com</id>
			<name>OpenKM Maven Repository</name>
			<url>http://maven.openkm.com/maven2</url>
		</repository>
	</repositories>
	<dependencies>
		<dependency>                                               <groupId>com.openkm</groupId>
			<artifactId>import-station-core</artifactId>
			<version>${openkm-import-station-core.version}</version>
		</dependency>
		<!-- Plugin framework -->
		<dependency>
			<groupId>com.google.code</groupId>
			<artifactId>jspf.core</artifactId>
			<version>${jspf.version}</version>
		</dependency>
		<!-- OpenKM SDK -->
		<dependency>
			<groupId>com.openkm</groupId>
			<artifactId>sdk4j</artifactId>
			<version>${openkm-sdk4j.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-log4j12</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>
	<build>
		<resources>
			<resource>
				<directory>src/main/html</directory>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-source-plugin</artifactId>
				<executions>
					<execution>
						<id>attach-sources</id>
						<goals>
							<goal>jar</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.19.1</version>
			</plugin>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>single</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

ExampleProcessor class:

Description: Imports only files from an specific source folder.

@PluginImplementation
public class ExampleProcessor implements PropertiesProcessor {

    @Override
    public boolean doJob(long executionTaskId, FormProperties formProperties, OKMWebservices webServices) {
        boolean returnValue = true;
        File folder = new File((String) formProperties.getCustomPropertiesMap().get(ProcessorConstants.USER_PATH));
        if (folder.exists() && folder.isDirectory()) {
            for (File file : folder.listFiles()) {
                if (file.isFile() && file.canRead()) {
                    try {
                        uploadDocument(file, formProperties, webServices);
                        ImportStationLogger.logOkEvent(executionTaskId, "File uploaded: " + file.getName());
                    } catch (IOException | UnsupportedMimeTypeException | FileSizeExceededException | UserQuotaExceededException
                            | VirusDetectedException | ItemExistsException | PathNotFoundException | AccessDeniedException
                            | RepositoryException | DatabaseException | ExtensionException | AutomationException
                            | UnknowException | WebserviceException e) {
                        ImportStationLogger.logErrorEvent(executionTaskId, "Error uploading file" + file.getName(), e);
                        returnValue = false;
                    }
                }
            }
        }                
        return returnValue;
    }

    private void uploadDocument(File file, FormProperties formProperties, OKMWebservices webServices) throws IOException, UnsupportedMimeTypeException,       FileSizeExceededException, UserQuotaExceededException, VirusDetectedException, ItemExistsException, PathNotFoundException, AccessDeniedException,       RepositoryException, DatabaseException, ExtensionException, AutomationException, UnknowException, WebserviceException {
        Document doc = new Document();

        doc.setPath(formProperties.getObjectProperty(ProcessorConstants.DESTINY_PATH)
                + ImportStationConstants.OKM_REPOSITORY_FILE_SEPARATOR + FilenameUtils.getBaseName(file.getName()) + "."
                + FilenameUtils.getExtension(file.getName()));

        // Create document
        webServices.createDocument(doc, FileUtils.openInputStream(file));        
    }

    @Override
    public String getAddTaskFormName() {
        return "ExampleProcessor-form.html";
    }

    @Override
    public String getDescription() {
        return "This is my custom form";
    }

    @Override
    public String getEditTaskFormName() {
        return "ExampleProcessor-form-edit.html";
    }

    @Override
    public Class<?> getFormBeanClass() {
        return ExampleFormProperties.class;
    }

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

}

 Bean code. Only stores user local path and OpenKM destiny path:

ExampleFormProperties class:

Descripcion:

  • Set source folder path.
  • Set OpenKM folder target.

public class ExampleFormProperties extends FormProperties {

    /**
     * Public constructor.
     * 
     * @param parameterMap
     *        map with values
     */
    public ExampleFormProperties(final Map<String, Object> parameterMap) {
        super(parameterMap);

        // Add custom properties
        addProperty(ProcessorConstants.USER_PATH,
                parameterMap.get(ProcessorConstants.USER_PATH) instanceof String[]
                        ? ((String[]) parameterMap.get(ProcessorConstants.USER_PATH))[0]
                        : parameterMap.get(ProcessorConstants.USER_PATH));
        addProperty(ProcessorConstants.DESTINY_PATH,
                parameterMap.get(ProcessorConstants.DESTINY_PATH) instanceof String[]
                        ? ((String[]) parameterMap.get(ProcessorConstants.DESTINY_PATH))[0]
                        : parameterMap.get(ProcessorConstants.DESTINY_PATH));
    }

}

ExampleProcessor-form.html

Description:

  • Form field source folder.
  • Form field target folder.

<div class="form-group">
	<label class="control-label col-sm-2" for="userPath">User path:</label>
	<div class="col-sm-10">
		<input type="text" class="form-control" name="userPath"
			placeholder="user path" required />
	</div>
</div>
<div class="form-group">
	<label class="control-label col-sm-2" for="destinyPath">Destiny path into OpenKM:</label>
	<div class="col-sm-10">
		<input type="text" class="form-control" name="destinyPath" placeholder="/okm:root/import/" required />
	</div>
</div>

ExampleProcessor-form-edit.html

Description:

  • Form field source folder.
  • Form field target folder.
Note: The fields have a class named "parameter" for automatic fields initialization.
<div class="form-group">
	<label class="control-label col-sm-2" for="userPath">User path:</label>
	<div class="col-sm-10">
		<input type="text" class="form-control parameter" name="userPath"
			value="${userPath}" required />
	</div>
</div>
<div class="form-group">
	<label class="control-label col-sm-2" for="destinyPath">Destiny path into OpenKM:</label>
	<div class="col-sm-10">
		<input type="text" class="form-control parameter" name="destinyPath" placeholder="/okm:root/import/" required />
	</div>
</div>