Skip to content

Commit

Permalink
[release] merging 'rl-1.0.0' into 'master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Barithel committed Mar 9, 2020
2 parents 48cbd07 + 186c895 commit 1b0a613
Show file tree
Hide file tree
Showing 89 changed files with 1,694 additions and 749 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ 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>0.1.0</version>
<version>1.0.0</version>
</dependency>
~~~

Expand All @@ -41,37 +41,37 @@ The filer defines 3 concepts:
* 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 node's classification:
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,
* *onUpdatePreoperties* and *onMoveNode* on a fileable node, to check for updates that could change the node's 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 supports 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 properties of the node.
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 would contain a particular description with a department data (e.g. department: treasury;) created in 2019 would be filed into the "treasury/2019" path inside the site's document library.
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:
```java
public class DepartmentFilerAction extends AbstractFilerAction {

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

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

@Override
Expand All @@ -82,7 +82,7 @@ public class DepartmentFilerAction extends AbstractFilerAction {
.folder().asSegment()
.named().with(node -> {
Pattern regex = Pattern.compile("department:\\s*(.+);");
Matcher matcher = regex.matcher(node.getProperty(ContentModel.PROP_DESCRIPTION, String.class));
Matcher matcher = regex.matcher(node.getProperty(ContentModel.PROP_DESCRIPTION, String.class).get());
matcher.find();
return matcher.group(1);
}).getOrCreate()
Expand All @@ -94,7 +94,7 @@ public class DepartmentFilerAction extends AbstractFilerAction {
```
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 inherits from [`AbstractFilerAction`].
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:
```xml
<bean id="you.name.it" parent="filer.action.base" class="your.implementation.XXXFilerAction"/>
Expand All @@ -104,12 +104,12 @@ 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.

## Properties inheritance
## Metadata inheritance

Another characteristic of this module is the ability to define which properties 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 properties duplicated.
First, the properties 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 properties they inherit.
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.
Expand Down
6 changes: 5 additions & 1 deletion alfresco-filer-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.atolcd.alfresco.filer</groupId>
<artifactId>alfresco-filer-parent</artifactId>
<version>0.1.0</version>
<version>1.0.0</version>
<relativePath>../alfresco-filer-parent/pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -59,6 +59,10 @@
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>content-services-community</artifactId>
Expand Down
2 changes: 2 additions & 0 deletions alfresco-filer-core/src/main/config/context/model-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
</property>
</bean>

<import resource="classpath:alfresco/module/*/filer/model-context.xml"/>

</beans>
24 changes: 11 additions & 13 deletions alfresco-filer-core/src/main/config/context/policy-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="filer.policy.fileableAspect" class="com.atolcd.alfresco.filer.core.policy.FileableAspect">
<property name="filerService" ref="filerService"/>
<property name="filerModelService" ref="filerModelService"/>
<property name="policyComponent" ref="policyComponent"/>
<bean id="filer.policy.base" abstract="true">
<constructor-arg ref="dictionaryDAO"/>
<constructor-arg ref="policyComponent"/>
<constructor-arg ref="filerModelService"/>
</bean>

<bean id="filer.policy.subscriberAspect" class="com.atolcd.alfresco.filer.core.policy.FilerSubscriberAspect">
<property name="filerService" ref="filerService"/>
<property name="filerModelService" ref="filerModelService"/>
<property name="policyComponent" ref="policyComponent"/>
<bean id="filer.policy.fileableAspect" parent="filer.policy.base" class="com.atolcd.alfresco.filer.core.policy.FileableAspect">
<constructor-arg ref="filerService"/>
<constructor-arg ref="NodeService"/>
</bean>

<bean id="filer.policy.segmentAspect" class="com.atolcd.alfresco.filer.core.policy.FilerSegmentAspect">
<property name="filerModelService" ref="filerModelService"/>
<property name="policyComponent" ref="policyComponent"/>
<property name="ownableService" ref="ownableService"/>
<property name="username" value="${filer.owner.username}"/>
<bean id="filer.policy.subscriberAspect" parent="filer.policy.base" class="com.atolcd.alfresco.filer.core.policy.FilerSubscriberAspect">
<constructor-arg ref="filerService"/>
</bean>

<bean id="filer.policy.segmentAspect" parent="filer.policy.base" class="com.atolcd.alfresco.filer.core.policy.FilerSegmentAspect"/>

</beans>
12 changes: 7 additions & 5 deletions alfresco-filer-core/src/main/config/context/scope-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="filer.scope.base" abstract="true">
<property name="filerRegistry" ref="filerRegistry"/>
<constructor-arg ref="filerRegistry"/>
</bean>

<bean id="filer.scope.defaultLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.DefaultFilerScopeLoader">
<property name="nodeService" ref="NodeService"/>
<constructor-arg ref="NodeService"/>
</bean>

<bean id="filer.scope.aspectsLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.AspectsFilerScopeLoader">
<property name="nodeService" ref="NodeService"/>
<constructor-arg ref="NodeService"/>
</bean>

<bean id="filer.scope.propertiesLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.PropertiesFilerScopeLoader">
<property name="nodeService" ref="NodeService"/>
<constructor-arg ref="NodeService"/>
</bean>

<bean id="filer.scope.siteLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.SiteFilerScopeLoader">
<property name="siteService" ref="siteService"/>
<constructor-arg ref="SiteService"/>
</bean>

<import resource="classpath:alfresco/module/*/filer/scope-context.xml"/>

</beans>
36 changes: 20 additions & 16 deletions alfresco-filer-core/src/main/config/context/service-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,31 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="filer" parent="abstractPropertyBackedBean"
class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory">
</bean>

<bean id="filerService"
class="com.atolcd.alfresco.filer.core.service.impl.FilerServiceImpl">
<property name="filerRegistry" ref="filerRegistry"/>
<property name="filerOperationService" ref="filerOperationService"/>
<property name="propertyInheritanceService" ref="filer.propertyInheritanceService"/>
<property name="nodeService" ref="NodeService"/>
<property name="permissionService" ref="PermissionService"/>
<property name="lockService" ref="lockService"/>
<property name="lockService" ref="LockService"/>
</bean>

<bean id="filerRegistry"
class="com.atolcd.alfresco.filer.core.service.impl.FilerRegistryImpl"/>

<bean id="filerModelService"
class="com.atolcd.alfresco.filer.core.service.impl.FilerModelServiceImpl">
<property name="behaviourFilter" ref="policyBehaviourFilter"/>
<property name="fileableAspectQName" value="${filer.aspect.fileable}"/>
<property name="segmentAspectQName" value="${filer.aspect.segment}"/>
<property name="subscriberAspectQName" value="${filer.aspect.subscriber}"/>
<property name="propertyInheritanceAspectQName" value="${filer.aspect.propertyInheritance}"/>
</bean>
<bean id="filerModelService" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory" ref="filer"/>
<property name="interfaces">
<list>
<value>com.atolcd.alfresco.filer.core.service.FilerModelService</value>
</list>
</property>
</bean>

<bean id="filerOperationService"
class="org.springframework.aop.framework.ProxyFactoryBean">
Expand Down Expand Up @@ -56,9 +60,9 @@

<bean id="filerFolderServiceImpl"
class="com.atolcd.alfresco.filer.core.service.impl.FilerFolderServiceImpl">
<property name="filerModelService" ref="filerModelService"/>
<property name="nodeService" ref="NodeService"/>
<property name="nodeDAO" ref="nodeDAO"/>
<constructor-arg ref="filerModelService"/>
<constructor-arg ref="NodeService"/>
<constructor-arg ref="nodeDAO"/>
</bean>

<bean id="filerUpdateService"
Expand Down Expand Up @@ -91,10 +95,10 @@

<bean id="filer.propertyInheritanceServiceImpl"
class="com.atolcd.alfresco.filer.core.service.impl.PropertyInheritanceServiceImpl">
<property name="filerModelService" ref="filerModelService"/>
<property name="nodeService" ref="NodeService"/>
<property name="dictionaryService" ref="dictionaryService"/>
<property name="dictionaryDAO" ref="dictionaryDAO"/>
<constructor-arg ref="dictionaryDAO"/>
<constructor-arg ref="filerModelService"/>
<constructor-arg ref="NodeService"/>
<constructor-arg ref="dictionaryService"/>
</bean>

<bean id="filer.action.base" abstract="true">
Expand Down
14 changes: 14 additions & 0 deletions alfresco-filer-core/src/main/config/disable/model-context.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- In order to disable the bootstrap of the default filer model :
1. create a file named "model-context.xml" in /alfresco/module/*/filer/model-context.xml
2. include this beans file : <import resource="classpath:alfresco/module/filer/disable/model-context.xml"/>
-->

<!-- Bean without any model, so this effectively performs NOOP -->
<bean id="filer.modelBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap" />

</beans>
27 changes: 27 additions & 0 deletions alfresco-filer-core/src/main/config/disable/scope-context.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- In order to disable the registration of the default filer scope :
1. create a file named "scope-context.xml" in /alfresco/module/*/filer/scope-context.xml
2. include this beans file : <import resource="classpath:alfresco/module/filer/disable/scope-context.xml"/>
-->

<!-- EmptyFilerScopeLoader, so this effectively performs NOOP -->
<bean id="filer.scope.defaultLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.EmptyFilerScopeLoader"/>

<!-- EmptyFilerScopeLoader, so this effectively performs NOOP -->
<bean id="filer.scope.aspectsLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.EmptyFilerScopeLoader"/>

<!-- EmptyFilerScopeLoader, so this effectively performs NOOP -->
<bean id="filer.scope.propertiesLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.EmptyFilerScopeLoader"/>

<!-- EmptyFilerScopeLoader, so this effectively performs NOOP -->
<bean id="filer.scope.siteLoader" parent="filer.scope.base"
class="com.atolcd.alfresco.filer.core.scope.impl.EmptyFilerScopeLoader"/>

</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

import org.alfresco.service.cmr.repository.NodeRef;

import edu.umd.cs.findbugs.annotations.CheckForNull;

public final class FilerFolderContext {

private final RepositoryNode node;
private final NodeRef parent;

@CheckForNull
private PropertyInheritance propertyInheritance;
private boolean enabled;

Expand All @@ -20,12 +23,8 @@ private FilerFolderContext(final RepositoryNode node, final NodeRef parent,
this.enabled = enabled;
}

public FilerFolderContext(final RepositoryNode node) {
this(node, null, null, true);
}

public FilerFolderContext(final FilerFolderContext other) {
this(other, other.parent);
public FilerFolderContext(final RepositoryNode node, final NodeRef parent) {
this(node, parent, null, true);
}

public FilerFolderContext(final FilerFolderContext other, final NodeRef parent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public String toString() {
return MessageFormat.format("Inbound'{'action={0}, name={1}, node={2}, store={3}'}'",
getAction().orElse(null),
getNode().getName().orElse(null),
getNode().getNodeRef().getId(),
getNode().getNodeRef().getStoreRef());
getNode().getNodeRef().map(NodeRef::getId).orElse(null),
getNode().getNodeRef().map(NodeRef::getStoreRef).orElse(null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@

import org.alfresco.service.namespace.QName;

import edu.umd.cs.findbugs.annotations.CheckForNull;

public class PropertyInheritance {

@CheckForNull
private Set<QName> mandatoryAspects; // Always apply
@CheckForNull
private Set<QName> optionalAspects; // Ignore if absent in the payload but always apply if present

public PropertyInheritance() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.alfresco.service.namespace.QName;
Expand All @@ -14,8 +13,8 @@ public class PropertyInheritancePayload {
private final Map<QName, Set<QName>> removed;

public PropertyInheritancePayload(final Map<QName, Map<QName, Serializable>> added, final Map<QName, Set<QName>> removed) {
this.added = Objects.requireNonNull(added);
this.removed = Objects.requireNonNull(removed);
this.added = added;
this.removed = removed;
}

@Override
Expand Down
Loading

0 comments on commit 1b0a613

Please sign in to comment.