Skip to content

Alfresco Content Services module to perform filer operations based on rules written with a Java fluent API

License

Notifications You must be signed in to change notification settings

atolcd/alfresco-filer

Repository files navigation

AtolCD Alfresco Filer

This is an Alfresco Content Services module to perform filer operations. It adds the ability to define rules to move an incoming or an updated document into the desired folder structure based on its characteristics (mainly its type, aspects and metadata). It offers a fluent API to define each level of the classification and whether they should be created on the fly. It also allows to inherit specific metadata that are defined at any level of the folder hierarchy to ensure consistency and availability.

Few things to notice

  • Runs with Alfresco Content Services 6.2 and JDK 11 (compatible with ACS 5.2 and JDK 8)
  • Standard JAR packaging and layout
  • AMP as an assembly
  • Tested with JUnit 5, Mockito 3 and PostgreSQL 10

Building and testing

The project can be built and tested by running the following Maven command:

mvn -Pdelivery clean package

Artifacts

The artifacts can be added to the dependency of your project in its pom.xml:

<dependency>
  <groupId>com.atolcd.alfresco.filer</groupId>
  <artifactId>alfresco-filer-core</artifactId>
  <version>1.1.0</version>
</dependency>

Using the module

The core of the module is based on Alfresco Content Services policies to detect nodes to be filed and an engine to execute filer actions.

Filer model

The filer defines 3 concepts:

  • a fileable: a node (document or folder) that can be automatically filed,
  • a filer subscriber: a container in which nodes are automatically filed,
  • a filer segment: a folder that is part of a hierarchy in which a node is filed, it can be deleted automatically if empty

The filer engine uses policies to detect changes in the repository and trigger its own rule mechanism to adapt the classification of the nodes:

  • onCreateChildAssociation on a filer subscriber, to label the incoming node as fileable,
  • onAddAspect on a fileable node, to trigger the initial classification,
  • onUpdateProperties and onMoveNode on a fileable node, to check for updates that could change its classification,
  • onDeleteNode on a fileable node, to remove a classification left empty.

Filer action

A filer action is evaluated by the filer engine to determine whether it applies to the node to be filed and then it performs the selected action.

First, it is required to provide the conditions upon which a filer action will be executed. The matching is actually performed in two passes to allow to quickly bypass classification if the node does not support a filer action based on some general requirements such as the containing site, its aspects or type. The second check allows for a more thorough inspection, including for example the metadata of the node. Finally, it is possible to define the action itself. This is indeed the actual classification, which would trigger the navigation or the creation of the folder structure.

Creating a filer action is done by implementing FilerAction or directly inheriting AbstractFilerAction.

Let's take a simple example where a document that contains a particular description with a department data (e.g. department: treasury;) created in 2019 should be filed into the "treasury/2019" path inside the document library of the site. Here is the corresponding implementation:

public class DepartmentFilerAction extends AbstractFilerAction {

  @Override
  public boolean supportsActionResolution(final FilerEvent event) {
    return event.getNode().getAspects().contains(ContentModel.ASPECT_TITLED)
        && event.getNode().getType().get().equals(ContentModel.TYPE_CONTENT);
  }

  @Override
  public boolean supportsActionExecution(final RepositoryNode node) {
    return node.getProperty(ContentModel.PROP_DESCRIPTION, String.class).orElse("").matches("department:.+;");
  }

  @Override
  protected void execute(final FilerBuilder builder) {
    builder.root(FilerNodeUtils::getSiteNodeRef)
        .folder()
            .named().with(SiteService.DOCUMENT_LIBRARY).get()
        .folder().asSegment()
            .named().with(node -> {
              Pattern regex = Pattern.compile("department:\\s*(.+);");
              Matcher matcher = regex.matcher(node.getProperty(ContentModel.PROP_DESCRIPTION, String.class).get());
              matcher.find();
              return matcher.group(1);
            }).getOrCreate()
        .folder().asSegment()
            .named().withPropertyDate(ContentModel.PROP_CREATED, "yyyy").getOrCreate()
        .updateAndMove();
  }
}

You can also look at example actions used in the tests and their corresponding folder structure creation put together in a dedicated service.

It is possible to create as many actions as needed. They are automatically registered by the FilerRegistry if they inherit from AbstractFilerAction. You just need to define the corresponding Spring bean:

  <bean id="you.name.it" parent="filer.action.base" class="your.implementation.XXXFilerAction"/>

You can also look at example beans used in the tests.

Actions are evaluated by the FilerService in order. They are first sorted by the explicit order defined in the action (FilerAction implements Ordered) and then alphabetically by bean name. The first action that matches the conditions is selected and its classification is applied.

Metadata inheritance

Another characteristic of this module is the ability to define which metadata should be inherited on the fileable node and also on the folder structure. It uses a specific marker aspect to label which aspects should have their metadata duplicated. First, the metadata of the inherited aspects are retrieved from the parent folder to supplement the node being filed. Then, each level of the classification can define the number of metadata they inherit.

For example, instead of using the description property, a custom aspect with a specific department label property can be set directly on the department folder. In this case any document created in it could also have the property directly added on them to make a search on the department of documents easier. The corresponding action implementation would look like this:

  @Override
  protected void execute(final FilerBuilder builder) {
    builder.root(FilerNodeUtils::getSiteNodeRef)
        .folder()
            .named().with(SiteService.DOCUMENT_LIBRARY).get()
        .folder(MyModel.TYPE_DEPARTMENT).asSegment()
            .mandatoryPropertyInheritance(MyModel.ASPECT_DEPARTMENT)
            .named().withProperty(MyModel.PROP_DEPARTMENT_LABEL).getOrCreate()
        .folder().asSegment()
            .named().withPropertyDate(ContentModel.PROP_CREATED, "yyyy").getOrCreate()
        .updateAndMove();
  }
}

About

Alfresco Content Services module to perform filer operations based on rules written with a Java fluent API

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages