Possible Content Model Enhancements

Document created by resplin Employee on Jun 6, 2015Last modified by alfresco-archivist on Aug 31, 2016
Version 4Show Document
  • View in full screen mode

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



This page describes ideas regarding enhancing the Alfresco content modelling capabilities in the future. Note that the content of this page relates to ideas - it does not describe what is already in the product, and does not constitute any commitment from Alfresco to deliver what is described. This page is being used (or has been used) by the engineers at Alfresco to record and share their thoughts on enhancing the content modelling capabilities of Alfresco.


Composite content


Model changes


The Alfresco content model allows a property to be defined as multi-value, but it is often the case that people want to have repeating groups of properties. To give a simple example, if one wanted to model a to-do list where each entry has the attributes 'task', 'category', and 'priority' then this would be a little awkward currently, probably entailing multiple 'task' nodes associated with one 'to-do list' node. What would be neater would be a mechanism whereby a single node could contain a repeating group of properties called 'tasks', with the property group containing the appropriate properties. The proposed way that this would be modelled is like this:



      <type name='myapp:ToDoList'>
         <title>To-Do List</title>
         <parent>cm:content</parent>
         <associations>
            <component-association name='myapp:task'>
                  <type>myapp:Task</class>
                  <mandatory>false</mandatory>
                  <many>true</many>
            </component-association>
         </associations>
      </type>

      <type name='myapp:Task'>
         <title>Task</title>
         <parent>cm:content</parent>
         <properties>
            <property name='myapp:taskName'>
               <title>Task</title>
               <type>d:text</type>
               <mandatory>true</mandatory>
            </property>
            <property name='myapp:taskPriority'>
               <title>Priority</title>
               <type>d:int</type>
               <mandatory>true</mandatory>
            </property>
            <property name='myapp:taskCategory'>
               <title>Category</title>
               <type>d:text</type>
               <mandatory>true</mandatory>
               <default>Default</default>
            </property>
         </properties>
      </type>

Note the new 'component-association'. This is what indicates that the child node is a component of the parent node - their lifecycles are entwined. When the parent is deleted/archived, the child is deleted/archived. When the parent is versioned, the child is versioned. When the parent is transferred, the child is transferred. When the parent is copied, the child is copied. In effect, a component association is a specialised parent-child association that imparts additional semantics. Using specialised parent-child associations has one nice advantage in that the child nodes are indexed so that XPath queries work. It also allows the 'component' to include associations itself, thus providing for repeating groups of both properties and associations.


Overview


For the purposes of this text we will name the parent of a component association the 'composite node' and the child of a component association the 'component node'. Note that it is possible for a component node to also be a composite node in the case where it is both the child of a component association and the parent of another component association. For any tree of composite nodes there is one 'root composite node' which is a composite node but not a component node:

600px

If we assume this modelling approach, then it's worth discussing the semantics imparted by a 'component association'. A component association is a parent-child association and must be the only parent-child association for which the component node is the child. The name of the association will always be the same as the type of the association (clearly, duplicate names are allowed). When copying a node that is the parent of a component child association, the child nodes of those associations must also be copied and associated with the parent copy in the same way. Ordering of the children must be maintained on the copy. The semantics of copy apply to checkout, version, and archive.

Search over composite nodes is another interesting topic. Do we support queries along the lines of:

  TYPE:{myapp}ToDoList @task.taskCategory:'Home'

interpreted as 'find me all nodes of type ToDoList that have a component ToDoTask that has a taskCategory of 'Home''. What about XPath queries?

Note that, in the example above, there is nothing precluding an instance of the 'myapp:Task' from being created 'standalone' (that is to say with no 'component' association to a parent 'myapp:ToDoList'). It is possible to move a node that is not a component so that it becomes a component, as long as doing so doesn't break the rule that a component node has precisely one parent. Equally, it is possible to move a component node so that it is no longer a component.

When an operation causes either the modified or modifier property to change on a component node (including its creation) then the same properties are similarly changed on all ascendant nodes up to and including the root of the composite node. The values of the properties on each affected node must be identical. That is to say that, using the ToDoList example above, if a component Task is updated so that its 'modified' property is affected then the 'modified' property of the parent ToDoList node is also updated to have the same value. Note that the use of exactly the same value is important - it is not acceptable for the parent node to have a modified time that is slightly later than the affected Task node. The relevant policies are triggered on all such affected nodes.

In order to assist with certain tasks (such as the propagation of the modified and modifier properties, for instance), all component nodes are decorated with a new aspect: 'sys:component'. This aspect holds two NodeRef properties: 'parentComposite' and 'rootComposite'. The value of parentComposite is the NodeRef of the node that contains this component. The value of rootComposite is the NodeRef of the node that forms the root of the composite that contains this node. If, for example, we extend the ToDoList model a little such that Task nodes may themselves be composed of SubTask nodes then each SubTask node would identify a Task node as its parentComposite and a ToDoList node as its rootComposite. Note that the ToDoList node itself is not a component, and therefore does not have the sys:component aspect. By this definition, in fact, the node referred to by the rootComposite of any component node will never itself be a component.


Updates to Foundation Services


This section identifies all changes needed to foundation service operations. Operations not identified here are assumed to function the same way as they currently do.


NodeService


addChild

A component node must be the child of precisely one component association and zero parent-child associations. addChild must fail if the childRef identifies a component node or if assocTypeQName identifies a component association.


createNode

The name of a component association will always be the name of the component association type. Therefore, the 'assocQName' argument will be ignored if 'assocTypeQName' identifies a component association.


getChildAssocs

Component associations are considered to be child associations, so a given component association will be returned by all variants of this operation when it meets the criteria specified by the parameters to the operation.


getChildByName / getChildrenByName

Component associations allow duplicate names, and therefore these two operations should not be expected to work when invoked with an assocTypeQName that identifies a component association.


getParentAssocs

Component associations are considered to be child associations, so a given component association will be returned by all variants of this operation when it meets the criteria specified by the parameters to the operation.


getPath / getPaths

A component association is a parent-child association, and therefore defines a path to its child component node. That path will be returned by these operations.


moveNode

Much like createNode, when moving a node so that it becomes a component of a composite node the assocQName argument is ignored. The name of the created component association will always be the same as its type.


removeAspect

It will not be possible to remove the sys:component aspect from a node using this operation.


restoreNode

Again, similar to both createNode and moveNode, the assocQName argument is ignored if assocTypeQName identifies a component association. The name of the created component association will always be the same as its type.


CopyService


copy

Much like NodeService.createNode, if the assocTypeQName argument identifies a component association then the assocQName argument is ignored and defaults to the name of the component association type.


LockService


lock

When this operation is invoked on a composite node then the lock is always propagated to that node's components. If the variant that accepts the boolean 'lockChildren' is invoked then this argument still controls whether the lock propagates to non-component child nodes, but does not affect the rule that it always propagates to component nodes.


unlock

This operation is symmetrical with the lock operation. That is to say that unlock on a composite node always propagates down through component nodes irrespective of whether 'unlockChildren' is set to true or false. The 'unlockChildren' flag continues to control whether the unlock should propagate to non-component child nodes.


CheckOutCheckInService


checkout (all variants)

If invoked on a composite node then the resulting working copy must also be a composite node that contains copies of the checked-out node's component nodes (and so on down the component tree if any component node is also a composite node). The Copy Service must be used to copy each node in the composite, thus ensuring that the cm:copiedfrom aspect is correctly applied and the cm:source property is correctly set:

600px


checkout(NodeRef, NodeRef, QName, QName)

Checking out a component node so that the working copy is not a component node is permitted:

600px

Checking out a node such that the working copy would be a component node is not permitted. That is to say that if the 'destinationAssocTypeQName' identifies a component association then this operation must fail.


checkout(NodeRef)

If this variant of the checkout operation is invoked on a component node then it must fail.


checkin (all variants)

This operation is symmetrical with checkout as described above. That is to say that checking in a working copy that is itself a composite node causes the state of all of its component nodes to be copied back to their equivalent nodes on the checked-out node.

There are a few cases that deserve special mention:


  1. If a new component node has been added to the working copy then a corresponding component node should be created on the checked-out node when it is checked in
  2. If a component node has been moved within the working copy (either reordered or moved within the composite tree) then this is reflected on the checked-out node when it is checked in
  3. If a component node has been removed from the working copy (either by deletion or by being moved outside of the composite tree of the working copy) then its corresponding node is also removed upon check in

VersionService


createVersion (all variants)

Versioning a component node is not permitted and must cause an exception to be thrown. Versioning a root composite always causes the creation of a version node that is also a root composite node with its component nodes reflecting the state of those of the original node. For each component node, the captured state includes properties, child associations, peer (node) associations, and aspects.


createVersion (NodeRef, Map, boolean)

In this variant of createVersion, the boolean argument 'versionChildren' continues to determine whether the versioning operation traverses child associations, but component associations are *always* traversed, irrespective of the value of this argument.


deleteVersion

Deleting a version of a composite node causes all the captured component nodes to be deleted too.




TransferService


Policies


Three new policies will be available for developers to hook behaviours on to: 'OnComponentAdded'; 'OnComponentUpdated'; and 'OnComponentDeleted'.


  • OnCreateComponent is triggered on a composite node when a new component node is created directly beneath it. The information posted on the event is the ChildAssociationRef describing the new association. Basically this mirrors the OnCreateNode event that's triggered on the component node.
  • OnUpdateComponent is triggered on a composite node when an operation occurs on a component node that causes an OnUpdateNode event to fire on it.
  • OnDeleteComponent is triggered on a composite node when an operation occurs on a component node that causes an OnDeleteNode event to fire on it.

Content Modeling
Engineering Notes

Outcomes