in Alfresco

Alfresco tutorial: Custom content types

Introduction

Alfresco is a flexible platform for developing content management applications. Fist step of designing custom content management is creating a custom model. Alfresco already has out-of-the-box content model, and probably this will be enough for most of the applications at least in the beginning .

In this tutorial we are going to go through how to extend alfresco out-of-the-box content model and add custom types, aspects, associations and properties.

Basics

Content model describes how the data is stored in the repository and it stands in a way of alfresco being a simple file system. Content model can be split into several key information:

  • Namespace, for each content model namespace is defined, http://alfrescoblog.com/model/content/1.0. Each name space has abbreviation in this case it is ab.
  • Types, content model can define several content types that can inherit some of the default content types already defined in alfresco out-of-the-box model. Each type is a part of defined namespace and it must have a name. Name is uniquely defined in this model and is unique in the system through namespace.
    Example: ab:invoice
  • Properties, each type can and should contain a array of properties, for an invoice type properties can be a invoice number, invoice date … Each property has its own name, type and defined constraints . Property type can be one of basic types like string, boolean, string and so on. But also can be of a other complex type like the one we defined earlier (ab:invoice)
  • Constraints, each property can, if required, have a constraint. Constraints are defined cross model, so they can be reused. There are 4 types of constraints:
    • REGEX, where value of the property should match the pattern.
    • LIST, where list of possible values are predefined
    • MINMAX, where range of numeric values is defined.
    • LENGTH, where string length is specified
  • Associations define connections between objects, these connections come in two flavors.
    • Peer associations, that define one object pointing to other , so in case one is deleted other continues to live on.
    • Child associations, define relationship where if parent is deleted so the child will be deleted too. Example of this is folder content association cm:contains where if a folder is deleted so it will the child be deleted also (cascade)
  • Aspects, when defining a custom type does not come as a right solution defining aspect can be handy. Aspect can be applied to a object and doing so aspects properties will be added to this object. If we create ab:invoice as aspect adding this to a document will ad custom properties like invoice number, invoice date …
  • Custom behaviours, are great thing that allows to predefine actions that are executed in cases of deleting, adding updating nodes. If we want to calculate an average value on invoices in a folder and put this value in folders custom property. We would create a custom behaviour on create and on delete invoice and recalculate this values on this events. Amazing ability, we of course can to this using a rule, but problem is that rule needs to be set on every folder required, this way we have a feature cross repository.

Best practices

  1. Do not change alfresco out-of-the-box content model
  2. Consider implementing a root scope type
  3. In the beginning add properties that you really need as it is hard to remove properties latter .
  4. Avoid unnecessary content depth
  5. Use aspect when you can
  6. Are empty types really necessary??
  7. Create multiple content models to keep them in order
  8. Implement one java class that corresponds with each of the custom models.

Define a custom model

Example of simple model is listed below in the file alfrescoBlogCustomModel.xml. It contains defines one aspect ab:invoice with two properties, invoiceDate and invoceNumber.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Definition of new Model -->
<model name="ab:contentmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
 <description>Alfresco Blog Custom Model</description>
 <author>Ares</author>
 <published>2014-05-17</published>
 <version>1.0</version>
 <imports>
    <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
    <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
 </imports>
 
 <namespaces>
    <namespace uri="http://www.alfrescoblog.com/model/data/1.0" prefix="ab" />
 </namespaces>

 <aspects>
   <aspect name="ab:invoice">
    <title>Invoice</title>
    <properties>
      <property name="ab:invNumber">
       <type>d:int</type>
      </property>
    </properties>
  </aspect>
 </aspects>
</model>

Register custom model

We have previously created a module called alfrescoblogAMPArch and instruction for this in Maven Alfresco SDK-tutorial . In file called service-context.xml should be added :

<bean id="myModule_dictionaryBootstrap" parent="dictionaryModelBootstrap"
     depends-on="dictionaryBootstrap">
   <property name="models">
     <list>
       <value>
             alfresco/module/${project.artifactId}/models/alfrescoBlogCustomModel.xml
       </value>
     </list>
   </property>
</bean>

Result of this would be deployed model in the repository after module is deployed. For module manipulation please read mentioned blog post.

Another way of deploying custom model is to add the file alfrescoBlogCustomModel.xml to Data Dictionary/models and after file has been uploaded open details and check Model Active.

Creating custom types

We are going to create invoice folder, containing one property ab:total that will hold total value of all invoices inside . Part of the model that defines a type is listed below.

<types>
  <type name="ab:invoiceFolder">
    <title>Invoice Folder</title>
    <parent>cm:folder</parent>
    <properties>
      <property name="ab:total">
        <type>d:float</type>
        <mandatory>true</mandatory>
        <default>0.0</default>
     </property>
   </properties>
  </type>
</types>

Every type definition can have

  • Parent type, used to define parent type that this custom type inherits. It can be any of the currently defined types, like cm:folder, cm:content and so on.
  • Title, used to define type name
  • List of properties
  • Mandatory aspects, example of mandatory aspect is listed below, this way we define list of mandatory aspects that are set by default when instance of this type is created. In this case we are adding cm:auditable.
             <mandatory-aspects>
                <aspect>cm:auditable</aspect>
             </mandatory-aspects>

 

  • List of associations, associations are connections between other nodes.

 

Creating custom aspect

Part of the model that defines custom aspect is listed below .

 <aspects>
   <aspect name="ab:invoice">
   <title>Invoice</title>
   <properties>
     <property name="ab:invDate">
       <type>d:datetime</type>
     </property>
     <property name="ab:invNumber">
       <type>d:int</type>
     </property>
   </properties>
  </aspect>
 </aspects>

Every aspect can contain

  • Aspect title
  • Unique name
  • List of properties
  • List of associations

 

Properties of aspect and types

Both aspects and types can and should contain a list properties, each of the property needs to have a name and its type. Here is a basic property of type text and name my:property.

<property name="my:property">
  <type>d:text</type>
</property>

There is a number of additional options that can be set to this property

  • Mandatory, to mark this property as mandatory
  • Default value, if initial value is not set the default value will be used as initial
  • Indexing options are used to specify if and how this value should be indexed , indexing can be disabled or enabled.
    • Atomic(default: true), defines if indexing should be done in transaction or it can be indexed in the background
    • Stored(false), if set to true then this value of this property can be obtained through Lucene low-level query API
    • Tokenized[true] , if true then value of this property will be tokenized before indexing
<property name="cm:content">
  <type>d:content</type>
  <mandatory>false</mandatory>
  <index enabled="true">
    <atomic>false</atomic>
    <stored>false</stored>
    <tokenised>true</tokenised>
  </index>
</property>

 

 

Associations

Using associations we can define connections with object. As we have said we have two types of associations, peer and child associations.

Peer Associations

Example of peer associations is subscribe ability, lets say that we have subscribe to folder action. We would need to let user subscribe to folder where he would like to follow new files that are added and be notified. What happens when folder gets deleted, should users be deleted to? Well of course not, this is then the case when peer associations should be used as on delete we do not have propagation.

 <aspect name="cm:subscribable">
         <associations>
            <association name="cm:subscribedBy">
               <source>
                  <mandatory>false</mandatory>
                  <many>true</many>
               </source>
               <target>
                  <class>cm:person</class>
                  <mandatory>false</mandatory>
                  <many>true</many>
               </target>
            </association>
         </associations>
      </aspect>

For this example we are going to see alfresco standard model part that explains this, we have an aspect that can be added to the node in this case folder(or file), association has a name, source and target parts. Target defines target type that is allowed, if it is mandatory and if can be many of. In this case folder can be created with 0 subscribed users and then they can be added as time goes.

Additionally we can specify if duplicates are allowed and then do not allow user to be subscribed twice on this folder. The propagateTimestamps element allows control over whether the modified timestamp of a parent should be modified if any of its children are modified.

<aspect name="cm:subscribable">
         <associations>
            <association name="cm:subscribedBy">
               <source>
                  <mandatory>false</mandatory>
                  <many>true</many>
               </source>
               <target>
                  <class>cm:person</class>
                  <mandatory>false</mandatory>
                  <many>true</many>
               </target>
              <duplicate>false</duplicate>
             <propagateTimestamps>false</propagateTimestamps>
            </association>
         </associations>
      </aspect>

 

Child associations

Main difference between child and peer associations is that in child associations parent owns the child. In case of deleting the parent children will be deleted to. We can also specify if duplicates are allowed and if timestamp propagations should be applied. Rest is the same as in peer associations.

 

 

This concludes tutorial on how to extend alfresco content model with few best practices. In future we are going to cover how to allow new properties to be seen in alfresco browse and share applications and how to create custom behaviours .

 

 

 

 

 

 

 

 

 

 

Don't be shellfish...Tweet about this on TwitterShare on LinkedInShare on Google+Share on RedditShare on Facebook

Was this helpful ?

  1. i want to inherit a content model into new content model so please tell me where i need to make changes. ?
    i have created a new content modek in which i have defined a new type which parent is atype1 from my parent content model, i have imported parent in to new content model . i have added entry of new content model in context file.
    but no luck.