Chapter 5. Content Modeling

WHAT'S IN THIS CHAPTER?

  • Defining a content model to represent your business domain

  • Registering your content model with the Alfresco content repository

  • Interacting with your content model via JavaScript

  • Understanding how a content model relates to the CMIS Data Model

Content modeling is a fundamental building block of the Alfresco content repository that provides a foundation for structuring content and working with content. It is an important concept to grasp, as nearly all solutions built with Alfresco require some form of content modeling.

The purpose of content modeling is to specify how nodes stored in the content repository are constrained. This imposes a formal structure on nodes that can be understood and enforced by an ECM application.

The storage engine of the content repository employs an uncomplicated data structure for recording entities, metadata, and relationships. The data structure is a tree of nodes where each node supports one or more properties whose values may be of any data type and either single- or multi-valued. A node has at least one parent (except for the root node, which has no parent) and may contain one or more child nodes. Nodes may also be related through arbitrary peer relationships. Although a straightforward data structure, its inherent simplicity allows the development of complex content constructs.

Content modeling puts the following constraints on the data structure:

  • A node must be of a given kind.

  • A node must carry an enumerated set of properties.

  • A property must be of a given data type.

  • A value must be within a defined set of values.

  • A node must be related to other nodes in a particular way.

These constraints allow the definition of (or modeling of) entities within the ECM domain. For example, many ECM applications are built around the notion of folders and documents (as show in Figure 5-1). It is content modeling that adds meaning to the node data structure.

FIGURE 5-1

Figure 5.1. FIGURE 5-1

Out of the box, Alfresco comes prepackaged with several content models for support of the common or standardized aspects of ECM, especially for Document and Records Management. You can accomplish a lot with just these models, but if you want to break beyond the classic file system, you'll need to roll up your sleeves and get modeling to support the specific needs of your ECM solution.

MODELING IN CONTEXT

Content modeling is really all about metadata (that is, data describing data). But one man's metadata is another man's data, so it can very quickly become confusing when discussing content models.

To put content modeling in context, Alfresco talks in terms of the four-layer metadata model, as shown in Figure 5-2.

FIGURE 5-2

Figure 5.2. FIGURE 5-2

The first layer, M0, represents the nodes, properties, and relationships held in the Alfresco content repository. These entities are managed through the various Content Repository Services, such as the File Folder service or CMIS (Content Management Interoperability Services).

The next layer, M1, is a content model that defines a set of related definitions to constrain the nodes in layer M0. Many content models may be registered with the content repository.

A content model is itself described by the next layer, M2, the content metamodel. Although this chapter is titled "Content Modeling," the majority of the chapter really focuses on the content metamodel, as this is how you express your new content models. If you do not understand the content metamodel, you cannot define a content model. There are already some standardized content metamodels: the CMIS Data Model and JSR-170 Node Type model. This chapter also explains how the content metamodel maps to these standards.

Finally, there is the most abstract layer, M3, the content metametamodel that supports the conversion of content models expressed in one content metamodel to another, such as from the CMIS Data Model to JCR Node Types. Out of the box, Alfresco does not provide a definition of the content metametamodel or a conversion tool, but there are third-party tools that specialize in this area.

So, in summary, you'll be learning all about the Alfresco content metamodel (M2), in order to define one or more content models (M1), to constrain the structure of your nodes (M0) held in your content repository.

DEPLOYING A CONTENT MODEL

A content model is defined in its entirety as a single XML document, which must comply with the content metamodel XSD schema provided by the Alfresco content repository. Each model contains a set of related and coherent definitions, and is deployed as a unit.

Several content models may be deployed to the content repository, and definitions in one content model may depend on definitions in another content model, allowing for the sharing of definitions.

There are two approaches to deploying a content model into the content repository: bootstrap and dynamic.

The bootstrap approach involves modifying Alfresco content repository XML configuration files in order to register the content model such that on startup of the content repository, the content model is read, validated, and registered. A repository component called the Dictionary Bootstrap is responsible for loading a specified list of content models and registering them with the content repository. To register new content models, it is necessary either to modify the content model list of an existing Dictionary Bootstrap component or to define a new Dictionary Bootstrap component. For encapsulated modular extensions, it is recommended to define a new Dictionary Bootstrap component. Content model .xml files are placed in the classpath.

A Dictionary Bootstrap component is defined using the following snippet of Spring framework XML:

<bean id="kbmodel.extension.dictionaryBootstrap" parent="dictionaryModelBootstrap"
    depends-on="dictionaryBoot strap">
  <property name="models">
    <list>
      <value>alfresco/extension/kbModel.xml</value>
    </list>
  </property>
</bean>

Code snippet kbModel-model-context.xml

This snippet must be added to an Alfresco extension context XML file following the usual Alfresco customization conventions. Content models provided out of the box by Alfresco are all registered this way.

Note

For a default installation of Alfresco, extension context XML files are placed into installLocation/tomcat/shared/classes/alfresco/extension.

Remember, with the bootstrap approach, changes to model definitions through the content-model XML file are only registered after restarting the content repository. This can lead to long development and test cycles, so the bootstrap approach should only be used once the model is baked.

For this reason, an alternate dynamic approach to deploying a content model is provided, allowing the registration and updates to content models without the need to restart the content repository to pick up the changes. Instead of placing content model XML files into the classpath, they are placed in the content repository itself under the folder

Company Home/Data Dictionary/Models

The easiest tool for this is Alfresco Explorer, which provides full access to the Alfresco content repository Data Dictionary folder. Upon creating or uploading a content model XML file, the model, by default, will not be active. To activate a model (that is, to auto-register it with the content repository), select the View Details option for the XML file and then select the Modify icon on the Properties pane. On the Modify Content Properties page, enable the Model Active checkbox.

To update a content model, simply edit its XML and save via Alfresco Explorer. If the model is active, the content repository will automatically register the changes with the content repository on save. If the content model XML file is checked out, the working copy will be ignored until it is checked in.

To deactivate a model, select the View Details option for the content model XML file and then select the Modify icon on the Properties pane. On the Modify Content Properties page, disable the Model Active checkbox. To remove a content model completely, simply delete the content model XML file.

There are restrictions on what changes can be made to a content model XML file and when a content model XML file can be deleted. Only incremental additions — changes that do not require modifications to existing data in the content repository — are allowed. The content model can be deleted only if it is not used by any data in the content repository.

THE CONTENT METAMODEL EXPLAINED

Now that you know how to register and deploy a content model XML file to the content repository, it is time to discover how to define a content model itself, which is expressed in terms of the content metamodel.

The content metamodel (as shown in Figure 5-3) is as follows:

FIGURE 5-3

Figure 5.3. FIGURE 5-3

At the heart of the content metamodel is a type. A type is just like a type or class in an object-oriented model. It represents objects in the real world with support for properties and the ability to inherit the definition of a parent type. Properties are named items of metadata associated with the type, where each property is of a given data type. Constraints can be applied to restrict the values of a property.

Relationships between types are modeled with associations, of which there are two types: child associations and peer associations. Child associations provide the ability to model compositions where the parent effectively owns the children and, therefore, operations like delete will propagate through to the children, just like a cascade delete in a relational database. Peer associations, on the other hand, define a relationship between two objects where neither object is superior to the other.

A concept that may not be so well understood is an aspect. The closest equivalent in the object-oriented world is the notion of multiple inheritance, where a type may inherit features from more than one parent type. In Alfresco, an aspect supports the same capabilities as a type, meaning it supports properties, and may be related to and may inherit the definition of a parent aspect. On the surface, aspects seem very similar to types, but there is one important distinguishing feature, which is that aspects may be shared across types. In other words, aspects allow cross-cutting of the content model, which is the sharing of property and association definitions by attaching them to multiple types. This is the content metamodel equivalent of multiple inheritance.

Content models constrain the structure of nodes held in the content repository. It is worth highlighting how nodes are tied to a content model. Each node, on creation, is given a type. The type may change over time, as long as the new type is compatible with the old type; however, a node is only ever of one type at any given time. In contrast, a node may be attached to one or more aspects. On creation, a node inherits the aspects attached to its type, as defined by the content model (at design time). But aspects go one step further. At runtime, it is possible to adjust the aspects attached to a node. This is a powerful feature, which allows content held in the content repository to be loosely constrained and for content to dynamically inherit features and capabilities.

The Metamodel XML Schema

The content metamodel is formally described by an XML-schema document. When in doubt about how to express a content model or understand the full capabilities of the content metamodel, it is highly recommended to interrogate the XML schema, as it provides the definitive description of the content metamodel.

Note

The latest content metamodel XSD schema can always be located at http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/repository/config/alfresco/model/modelSchema.xsd

The target namespace of the content metamodel XML schema is

www.alfresco.org/model/dictionary/1.0

Enough of the theory: It's time to learn how to define a content model using each of the content metamodel capabilities.

The Model Header

Each content model starts with a model header, which provides an introduction to the model, composed of description, author, publication date, and version number. All parts of the model introduction are optional.

<xs:element name="model">
  <xs:attributeGroup ref="name" />
  ...
  <xs:complexType>
    <xs:sequence>
      <xs:element name="description" type="string"maxOccurs="1" minOccurs="0" />
      <xs:element name="author" type="string"maxOccurs="1" minOccurs="0" />
      <xs:element name="published" type="date"maxOccurs="1" minOccurs="0" />
      <xs:element name="version" type="string"maxOccurs="1" minOccurs="0" />
      ...
    </xs:sequence>
    ...
  </xs:complexType>
  ...
  <xs:attributeGroup name="name">
    <xs:attribute name="name" type="string" use="required" />
  </xs:attributeGroup>
</xs:element>

Code snippet modelSchema.xsd

A content model must be uniquely named within a given content repository. To assist with the definition of unique names across all content repositories, the content metamodel supports the notion of a namespace.

Model Namespaces

Namespaces provide a mechanism for specifying globally unique names for definitions within content models. A namespace is composed of a URI (a unique string often prefixed with an HTTP address associated with the author) and a Prefix (a shorthand code for the URI). Alfresco has defined several namespaces for the models provided out of the box with the content repository. The prefix for those namespace URIs is www.alfresco.org.

To associate a name with a namespace, it is only necessary to prefix the name with the relevant namespace prefix. For example, if the namespace URI http://example.org/contentmodels and associated prefix ex are defined, then the name ex:customtype means that customtype is a name defined within the namespace http://example.org/contentmodels.

Each content model must define at least one namespace for the names defined in that content model.

<xs:element name="model">
  ...
  <xs:element name="namespaces">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="namespace" maxOccurs="unbounded" minOccurs="1">
          <xs:complexType>
            <xs:attributeGroup ref="namespaceDefinition" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
  <xs:attributeGroup name="namespaceDefinition">
    <xs:attribute name="uri" type="string" use="required" />
    <xs:attribute name="prefix" type="string" use="required" />
  </xs:attributeGroup>
  ...
</xs:element>

Code snippet modelSchema.xsd

Quite often, it is necessary for a content model to refer to definitions that reside in another content model. For example, a type may inherit from a type defined in another content model.

To refer to names defined outside of the content model, it is necessary to import the namespace within which that name is defined. Once imported, the name can be used just as if the content model itself defined it.

<xs:element name="model">
  ...
  <xs:element name="imports" maxOccurs="1" minOccurs="0">
    <xs:complexType>
      <xs:sequence>
<xs:element name="import" maxOccurs="unbounded" minOccurs="1">
          <xs:complexType>
            <xs:attributeGroup ref="namespaceDefinition" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:element>

Code snippet modelSchema.xsd

On import of a namespace URI, it is possible to remap the namespace prefix defined in the originating content model just in case there are prefix clashes between imported namespaces.

The content repository does not allow a content model to reference names that do not exist.

Types

A content model may define one or more types.

<xs:element name="model">
  ...
  <xs:element name="types" maxOccurs="1" minOccurs="0">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="type" type="type" maxOccurs="unbounded" minOccurs="1" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:element>

Code snippet modelSchema.xsd

Each type is uniquely named and is optionally labeled with a title and description. A type may declare any number of properties to represent metadata associated with the type and any number of associations to other types.

<xs:complexType name="type">
  <xs:complexContent>
    <xs:extension base="class"/>
  </xs:complexContent>
</xs:complexType>

<xs:complexType name="class">
  ...
<xs:attributeGroup ref="name" />
  ...
  <xs:sequence>
    ...
    <xs:group ref="TextualDescription"/>
    <xs:element name="properties" maxOccurs="1" minOccurs="0">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="property" type="property"maxOccurs="unbounded"
          minOccurs="0" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:element name="associations" maxOccurs="1" minOccurs="0">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="association" type="association"
          maxOccurs="unbounded" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
    ...
  </xs:sequence>
</xs:complexType>

<xs:group name="TextualDescription">
  <xs:sequence>
    <xs:element name="title" type="string" maxOccurs="1" minOccurs="0" />
    <xs:element name="description" type="string" maxOccurs="1" minOccurs="0" />
  </xs:sequence>
</xs:group>

Code snippet modelSchema.xsd

You may have observed that the schema definition of a type is simply a derivation of the schema definition named class. The reason for this will become apparent when the definition of an aspect is discussed.

The final feature of a type definition is to allow control over whether nodes of that type are archived when deleted.

<xs:complexType name="class">
  ...
  <xs:element name="archive" type="boolean" maxOccurs="1" minOccurs="0" />
  ...
</xs:complexType>

Code snippet modelSchema.xsd

Archived nodes may be restored just like the recycle bin of many operating systems.

Properties

Each property must be uniquely named and is optionally labeled with a title and description.

<xs:complexType name="property">
  <xs:attributeGroup ref="name" />
  <xs:sequence>
    <xs:group ref="TextualDescription" />
    <xs:element name="type" type="string" />
    <xs:element name="mandatory" maxOccurs="1" minOccurs="0" type="mandatoryDef" />
    <xs:element name="multiple" type="boolean" maxOccurs="1" minOccurs="0" />
    <xs:element name="default" type="anyType" maxOccurs="1" minOccurs="0" />
    <xs:element name="index" maxOccurs="1" minOccurs="0">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="atomic" type="boolean" maxOccurs="1" minOccurs="0" />
          <xs:element name="stored" type="boolean" maxOccurs="1" minOccurs="0" />
          <xs:element name="tokenised" maxOccurs="1" minOccurs="0" >
            <xs:simpleType>
              <xs:restriction base="string">
                <xs:enumeration value="true"/>
                <xs:enumeration value="false"/>
                <xs:enumeration value="both"/>
              </xs:restriction>
            </xs:simpleType>
          </xs:element>
        </xs:sequence>
        <xs:attribute name="enabled" type="boolean" use="required" />
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="mandatoryDef" mixed="true">
  <xs:attribute name="enforced" use="optional" type="boolean"/>
</xs:complexType>

Code snippet modelSchema.xsd

The only feature of a property that must be specified is its data type, of which the content repository supports a wide variety. Each data type is named, and the commonly used data types out of the box are:

  • d:text—A text value, a character string

  • d:mltext—A multilingual text value where many localized representations of the text value may be held

  • d:content—An arbitrarily long text or binary stream

  • d:int—An integer value (java.lang.Integer equivalent)

  • d:long—A long value (java.lang.Long equivalent)

  • d:float—A float value (java.lang.Float equivalent)

  • d:double—A double value (java.lang.Double equivalent)

  • d:date—A date value (java.lang.Date equivalent)

  • d:datetime—A date and time value (java.lang.Date equivalent)

  • d:boolean—A boolean value (java.lang.Boolean equivalent)

  • d:any—Any value, regardless of type

With the basics defined, it is possible to fine-tune the definition of a property. By default a property supports a single value, but this may be changed to support multiple values via the multiple element. Multiple values are rendered as lists in the various Alfresco APIs.

A value can also be mandated on creation of a node via the mandatory element. That is, a transaction will not commit unless all mandatory values have been provided for nodes modified within that transaction. There are actually two forms of mandatory: enforced and relaxed. Enforced is as described but relaxed gives finer control over when mandatory values must be provided. A transaction will still commit if a relaxed mandatory value has not been specified; however, the node with the missing value will be marked as incomplete (via the sys:incomplete aspect). This is important, as not all content creation processes (for example, via many of the protocols supported by Alfresco) provide the ability to set property values. Custom solutions can then be configured or built that trigger a business process for collecting the missing values (for example, via workflow user-assigned tasks).

In conjunction with mandating a value, a property definition can also specify a default value that is set automatically by the content repository if the value has not been set at transaction commit time.

To enable the content repository to query on a property value, the property must first be indexed, which is controlled via the index element. It is recommended to choose carefully which properties are indexed, as each property will increase the size of the index. Properties may be indexed as part of the transaction commit (known as atomic indexing) or indexed in the background. Typically, properties of type d:content are background-indexed. Control over how values are tokenized is also possible, such that only the tokenized value is stored in the index, or only the original value is stored in the index, or both. Tokenization is the process used by the query engine for recognizing words and other elements, such as punctuation.

Although the content model provides the ability to specify property definitions, the content repository supports a feature known as a residual property, where a value can be set on a node for which there is no associated property definition defined in a content model. This allows for a node to act as a property bag for an arbitrary set of named-value pairs.

Associations

Each association must be uniquely named and is optionally labeled with a title and description.

<xs:complexType name="association">
  <xs:attributeGroup ref="name" />
  <xs:sequence>
    <xs:group ref="TextualDescription" />
    <xs:element name="source" maxOccurs="1" minOccurs="0">
<xs:complexType>
        <xs:sequence>
          <xs:element name="mandatory" type="boolean"maxOccurs="1" minOccurs="0" />
          <xs:element name="many" type="boolean"maxOccurs="1" minOccurs="0" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
    <xs:element name="target">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="class" type="string" maxOccurs="1" minOccurs="1" />
          <xs:element name="mandatory" type="mandatoryDef"maxOccurs="1"
          minOccurs="0" />
          <xs:element name="many" type="boolean"maxOccurs="1" minOccurs="0" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

Code snippet modelSchema.xsd

Associations are always between two types — a source type and a target type — where the source type is inherently the type that defines the association. The only feature of an association that must be specified is its target type via the class element on the target end.

Each end of the association may be fine-tuned by specifying features such as cardinality via the mandatory and many elements:

  • 0 or 1 => mandatory = false and many = false

  • 1 => mandatory = true and many = false

  • 0 or more => mandatory = false and many = true

  • 1 or more => mandatory = true and many = true

As with mandatory properties, both enforced and relaxed modes are supported for the target end, allowing control over whether a transaction can commit or not based on missing associations. In relaxed mode, the incomplete aspect is added to source nodes where associations to target nodes are missing.

Child associations are defined in the same way as peer associations, with a few additional features.

<xs:complexType name="childAssociation">
  <xs:complexContent>
    <xs:extension base="association">
      <xs:sequence>
        <xs:element name="duplicate" type="boolean"maxOccurs="1" minOccurs="0" />
<xs:element name="propagateTimestamps" type="boolean"maxOccurs="1"
        minOccurs="0" />
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

Code snippet modelSchema.xsd

First, the uniqueness of child-node names within the parent may be specified via the duplicate element. If duplicate is true, the content repository will not allow the commit of a transaction where duplicate node names are found within a set of children for a given single parent.

Second, the propagateTimestamps element allows control over whether the modified timestamp of a parent should be modified if any of its children are modified.

Constraints

Constraints can be defined either standalone, allowing for the reuse of constraints across many properties, or inline, where the constraint is defined specifically for a single property.

<xs:complexType name="constraint">
  <xs:attribute name="name" use="optional" type="string" />
  <xs:attribute name="type" type="string" use="optional"/>
  <xs:sequence>
    <xs:element name="parameter" maxOccurs="unbounded" type="namedValue"
    minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="ref" use="optional" type="string" />
</xs:complexType>

<xs:complexType name="namedValue">
  <xs:attributeGroup ref="name" />
  <xs:choice>
    <xs:element name="value" type="string" maxOccurs="1"minOccurs="0" />
    <xs:element name="list" maxOccurs="1" minOccurs="0">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="value" type="string"maxOccurs="unbounded"
          minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>
  </xs:choice>
</xs:complexType>

Code snippet modelSchema.xsd

A standalone constraint must specify a unique name and a type. There are several constraint types provided out of the box; the commonly used types are:

  • REGEX—Property value matches regular expression.

  • LENGTH—Text property value length must reside within minimum and maximum length limits.

  • MINMAX—Numeric property value must reside within minimum and maximum range limits.

  • LIST—Property value must be one of those specified in the list of values.

Custom constraint types may be developed and registered with the content repository.

Each constraint type is parameterized via zero or more parameter elements, where the parameter names are specific to each type:

  • REGEX—Expression

  • LENGTHminLength and maxLength

  • MINMAXminValue and maxValue

  • LISTallowedValues and caseSensitive

A content model may define one or more standalone constraints.

<xs:element name="model">
  ...
  <xs:element name="constraints" maxOccurs="1" minOccurs="0">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="constraint" type="constraint" maxOccurs="unbounded"
          minOccurs="1" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:element>

Code snippet modelSchema.xsd

Each property may support one or more constraints either by referencing an existing standalone constraint definition or defining one inline via the constraint element.

<xs:complexType name="property">
  ...
  <xs:element name="constraints" maxOccurs="1" minOccurs="0">
<xs:complexType>
      <xs:sequence>
        <xs:element name="constraint" type="constraint" maxOccurs="unbounded"
          minOccurs="1" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:complexType>

Code snippet modelSchema.xsd

An existing constraint is referenced via the ref element, whose value is the name of the constraint to reference. Otherwise, an inline constraint is defined in the same manner as a standalone constraint.

Inheritance

A type may inherit its definition from another type. What does that actually mean? All features of the parent type are inherited, including property, association, and constraint definitions — except for the parent type name, title, and description.

<xs:complexType name="class">
  <xs:sequence>
    ...
    <xs:element name="parent" type="string" maxOccurs="1" minOccurs="0" />
    ...
  </xs:sequence>
</xs:complexType>

Code snippet modelSchema.xsd

A type is said to inherit from another type when its parent element is populated with the name of the parent type to inherit. The inheriting type is often referred to as the subtype, while its parent is often referred to as the super-type. Inheritance may be nested, so it is possible to inherit from a type which itself inherits from another type.

Subtypes have the freedom to specify further property, association, and constraint definitions in addition to those inherited from its parent. However, in some cases, it is useful to refine a definition inherited from its parent. For example, a parent type may support an optional property, which the subtype wishes to lock down by mandating its value.

It is not possible to refine all inherited definitions, as it would be very easy to define an incoherent content model. For that reason, the content metamodel provides a fixed set of refinements known as property overrides.

<xs:complexType name="class">
  ...
  <xs:element name="overrides" maxOccurs="1" minOccurs="0">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="property" type="propertyOverride" minOccurs="1" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:complexType>

<xs:complexType name="propertyOverride">
  <xs:attributeGroup ref="name" />
  <xs:sequence>
    <xs:element name="mandatory" type="boolean" maxOccurs="1" minOccurs="0" />
    <xs:element name="default" type="string" maxOccurs="1" minOccurs="0" />
    <xs:element name="constraints" maxOccurs="1" minOccurs="0">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="constraint" type="constraint" minOccurs="1" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

Code snippet modelSchema.xsd

Each property override is given the same name as the property it wishes to override from its parent type. The following property features may be overridden:

  • mandatory — A subtype may enforce a property to become mandatory but it cannot relax an existing parent mandatory constraint.

  • default — A subtype may introduce a default value or change an existing parent default value.

  • constraints — Additional constraints may be applied to a parent property, but existing constraints cannot be modified.

Aspects

Aspects allow property and association definitions to be shared across many types of node. This means a cross-cutting feature of an ECM domain model may be encapsulated and applied throughout the rigid part of the model represented by types. It is the equivalent of multiple inheritance.

A node in the content repository must be of a single type, but may be attached to one or more aspects. The aspects are either inherited from its type (as defined in the content model), or can be attached or detached at runtime, allowing a node to dynamically inherit features and capabilities.

Each content model may define one or more aspects.

<xs:element name="model">
  ...
  <xs:element name="aspects" maxOccurs="1"minOccurs="0">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="aspect" type="aspect"maxOccurs="unbounded"
        minOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:element>

Code snippet modelSchema.xsd

Aspects support all the same features as types and therefore are defined in the same way as types.

<xs:complexType name="aspect">
  <xs:complexContent>
    <xs:extension base="class" />
  </xs:complexContent>
</xs:complexType>

Code snippet modelSchema.xsd

You can see that, as with the type definition, an aspect definition is simply a derivation of the schema definition named class. This means that an aspect shares all the same features as a class, including property, association, and constraint definitions. Aspects may inherit from parent aspects and support property overrides.

An aspect may be attached to one or more types. Remember, this means that a node created of that type automatically inherits the attached aspects.

<xs:complexType name="class">
  ...
  <xs:element name="mandatory-aspects" maxOccurs="1" minOccurs="0">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="aspect" type="string" maxOccurs="unbounded"
        minOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  ...
</xs:complexType>

Code snippet modelSchema.xsd

Attaching an aspect simply requires specifying the name of the aspect to attach in the aspect element of the source type. You may have noticed this feature is available at the class level, allowing aspects to be attached to other aspects as well as types.

OUT-OF-THE-BOX MODELS

The content repository comprises several content models provided out of the box for specifying the core content types expected of an ECM system. They are expressed in terms of the content metamodel and provide an excellent set of samples on which to base your own custom content models.

The base model upon which all other models depend is the Data Dictionary model (located in the file dictionaryModel.xml), which provides definitions for the fundamental data types, such as d:text and d:boolean. It exposes the namespace URI www.alfresco.org/model/dictionary/1.0 with prefix d.

Next, the content repository itself depends on a system model (located in the file systemModel.xml), which provides definitions for types used by the implementation of the content repository, such as sys:base, sys:root, and sys:reference. In most cases, it should not be required to refer to definitions in the system model from your own custom models. It exposes the namespace URI www.alfresco.org/model/system/1.0 with prefix sys.

Finally, an ECM domain model (located in the file contentModel.xml) provides definitions for types influenced by the CMIS and JCR standards, such as cm:folder, cm:content, cm:versionable, and cm:auditable. All Alfresco Content Application Server services, protocols, and clients are focused on these types. It exposes the namespace www.alfresco.org/model/content/1.0 with prefix cm.

A STEP-BY-STEP CUSTOM MODEL

Now that you have an understanding of the content metamodel, it is time to define a custom model: an uncomplicated model for representing knowledge base articles for the Knowledge Base application.

The Knowledge Base model (as shown in Figure 5-4) utilizes most of the constructs of the content metamodel. The primary entity in the model is the knowledge article, which encapsulates knowledge in a form such as an answer (to a frequently asked question) or a white paper. Articles may relate to each other. Supplementary artifacts, such as software patches and sample code, may be attached to the article.

FIGURE 5-4

Figure 5.4. FIGURE 5-4

It has been decided to define a type for the attachment, which inherits from the cm:content type defined in the ECM domain model. A single additional property is used to represent the attachment type whose value is constrained to one of Patch, Sample, and Documentation.

An aspect is defined for the article with the intent that any existing content held in the content repository may become an article. The aspect defines an article type property whose value is constrained to one of Article, FAQ, and White Paper. It also includes a status property through the inclusion of a status aspect whose value is constrained to one of Draft, Pending Approval, Current, and Archived.

A child association is ideal for representing the relationship between article and attachment. Attachments are owned by articles and therefore do not outlive their owning article, which child association semantics support. Related articles are peers and one is not superior to the other; thus a peer association is ideal for representing this relationship.

The first step in defining any content model is to set up the model header to introduce the model.

<model name="kb:contentmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
  <description>Knowledge Base Content Model</description>
  <author>alfresco_professional</author>
  <version>1.0</version>

Code snippet kbModel.xml

Model names are scoped by a namespace. In this case, the model name kb:knowledgebase uses the namespace prefix kb. However, its associated namespace has yet to be defined, which is the next step.

<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.example.org/knowledgebase" prefix="kb"/>
  </namespaces>

Code snippet kbModel.xml

As well as defining a new namespace, two out-of-the-box content models (the Data Dictionary and ECM domain model) are imported.

It is now possible to start defining the features of the model. First, standalone constraints are defined to restrict the values of attachment type, status, and article type, which are ideally constrained through the LIST constraint.

<constraints>
    <constraint name="kb:attachmenttype_constraint" type="LIST">
      <parameter name="allowedValues">
        <list>
          <value>Patch</value>
          <value>Sample</value>
          <value>Documentation</value>
        </list>
      </parameter>
    </constraint>
    <constraint name="kb:status_constraint" type="LIST">
      <parameter name="allowedValues">
        <list>
          <value>Draft</value>
          <value>Pending Approval</value>
          <value>Current</value>
          <value>Archived</value>
        </list>
      </parameter>
    </constraint>
    <constraint name="kb:articletype_constraint" type="LIST">
      <parameter name="allowedValues">
        <list>
          <value>Any</value>
          <value>Article</value>
          <value>FAQ</value>
          <value>White Paper</value>
        </list>
      </parameter>
    </constraint>
  </constraints>

Code snippet kbModel.xml

Everything is now set up to define the attachment type with its single property, whose data type is d:text and whose value is constrained through the standalone constraint kb:attachmenttypelist defined earlier in the model. For completeness, a default value is also specified.

<types>
    <type name="kb:attachment">
      <title>Attachment</title>
      <parent>cm:content</parent>
      <properties>
        <property name="kb:attachmenttype">
          <title>Attachment Type</title>
          <type>d:text</type>
          <default>Sample</default>
          <constraints>
            <constraint ref="kb:attachmenttype_constraint" />
          </constraints>
</property>
      </properties>
    </type>
  </types>

Code snippet kbModel.xml

Through namespace prefixes, references to imported definitions are simply made, such as to the d:text data type and cm:content type. The article aspect is defined in a similar manner to the attachment type, only this time an inline constraint is specified for its property.

<aspects>
    <aspect name="kb:article">
      <title>Knowledge Base Article</title>
      <properties>
        <property name="kb:articletype">
          <title>Article Type</title>
          <type>d:text</type>
          <default>Article</default>
          <constraints>
            <constraint type="LIST">
              <parameter name="allowedValues">
                <list>
                  <value>Any</value>
                  <value>Article</value>
                  <value>FAQ</value>
                  <value>White Paper</value>
                </list>
              </parameter>
            </constraint>
          </constraints>
        </property>
      </properties>
      ...

Code snippet kbModel.xml

Associations are between a source and target class. In this case, the article aspect will represent the source and therefore provide the association definitions.

...
      <associations>
        <child-association name="kb:artifacts">
          <target>
            <class>kb:attachment</class>
            <mandatory>false</mandatory>
            <many>true</many>
          </target>
<duplicate>true</duplicate>
        </child-association>
        <association name="kb:related">
          <title>Related Articles</title>
          <source>
            <mandatory>false</mandatory>
            <many>true</many>
          </source>
          <target>
            <class>kb:article</class>
            <mandatory>false</mandatory>
            <many>true</many>
          </target>
        </association>
      </associations>
    </aspect>
  </aspects>

Code snippet kbModel.xml

Finally, the model is closed.

</model>

Code snippet kbModel.xml

The preceding XML may be saved to a file on the Java classpath for registration by a Dictionary Bootstrap component at repository startup time or saved as a file in the content repository folder Company Home/Data Dictionary/Models for dynamic registration.

CREATING CONTENT WITH JAVASCRIPT

With the example content model defined, it is now possible to create articles within the content repository that adhere to the model. The best way to demonstrate this is to develop some JavaScript code that uses the Alfresco JavaScript API to create knowledge articles.

  1. You first need to log in to Alfresco Explorer.

    1. Type the following in your Web browser, and log in with the user name admin and password admin if requested:

      http://localhost:8080/alfresco
    2. Navigate to Company Home > Data Dictionary > Scripts.

  2. Now create a JavaScript file.

    1. In the Create menu, click Create Content.

    2. Enter the name for the JavaScript in the Name field, such as:

      kb.js
    3. In the Content Type list, select Plain Text.

    4. Click Next.

    5. Type the following in the Enter Content box:

      var article = companyhome.createNode("article", "cm:content");
      article.addAspect("kb:article");
      
      article.properties["cm:name"] = "How to Create Content Models";
      article.properties["kb:articletype"] = "FAQ";
      
      article.properties["wordcount"] = 7000;
      article.content = "The attached tutorial provides an overview of how to...";
      article.save();
      
      var attachment = article.createNode("attachment", "kb:attachment",
      "kb:artifacts");
      attachment.properties["cm:name"] = "Content Modeling Tutorial";
      attachment.properties["kb:attachmenttype"] = "Documentation";
      attachment.content = "Content modeling is a fundamental building block...";
      attachment.save();
      
      var relatedarticle = companyhome.createNode("relatedarticle", "cm:content");
      relatedarticle.addAspect("kb:article");
      relatedarticle.properties["cm:name"] = "Model Schema Reference";
      article.createAssociation(relatedarticle, "kb:related");
      relatedarticle.save();

      Code snippet kb.js

    6. Click Next.

    7. Click Finish.

    8. Click OK.

  3. Next, execute the JavaScript.

    1. In the More Actions menu, click View Details.

    2. Click Run Action.

    3. In the Select Action list, select Execute a script.

    4. Click the Set Values and Add button.

    5. In the Select a Script to Execute list, select kb.js.

    6. Click OK.

    7. Click Finish.

    8. Click Close.

  4. Finally, it's time to test.

    1. Navigate to Company Home.

    2. If you see two content items, one named How To Create Content Model and the other named Model Schema Reference, your JavaScript is working.

It's time to take a deeper look at the JavaScript code and how the Alfresco JavaScript API is used. The first step is to create a node of a given type from the model and then attach an aspect from the model at runtime. All nodes have a parent (except for the system root node), so implicit child association is created between the new node and its chosen parent. By default, the JavaScript createNode method creates a cm:contains child association as defined in the ECM domain model. Access to the content repository Company Home folder is provided, so your article can be created as a child of this folder.

var article = companyhome.createNode("article", "cm:content");
article.addAspect("kb:article");

Code snippet kb.js

With the node created, it is possible to set property values for property definitions as specified by the types and aspects from the model. Note that some of the properties, such as cm:name, are inherited. Default values are applied to properties that have not been set.

article.properties["cm:name"] = "How to Create Content Models";
article.properties["kb:articletype"] = "FAQ";

Code snippet kb.js

Remember, it is also possible to set properties that do not have an associated property definition, known as residual properties.

article.properties["wordcount"] = 7000;

Code snippet kb.js

Properties of type d:content are treated specially by the JavaScript API, which provides support for setting those values either from a content stream or a string.

article.content = "The attached tutorial provides an overview of how to...";

Code snippet kb.js

When creating a child node, you may override the default cm:contains child association by specifying the name of the custom child association to use.

var attachment = article.createNode("attachment", "kb:attachment", "kb:artifacts");
attachment.properties["cm:name"] = "Content Modeling Tutorial";
attachment.properties["kb:attachmenttype"] = "Documentation";
attachment.content = "Content modeling is a fundamental building block...";

Code snippet kb.js

Peer associations between nodes may be established via the JavaScript createAssocation method. The target node and association name are specified.

var relatedarticle = companyhome.createNode("relatedarticle", "cm:content");
relatedarticle.addAspect("kb:article");
relatedarticle.properties["cm:name"] = "Model Schema Reference";
article.createAssociation(relatedarticle, "kb:related");

Code snippet kb.js

Finally, the nodes are saved and the transaction is committed. If any of the property values happen to violate an associated constraint and the constraint is enforced, the transaction will not commit, meaning an error is raised. Otherwise, the transaction commits and the nodes are persisted in the content repository.

MODEL LOCALIZATION

Every type, aspect, property, association, constraint, and data type defined within a model has a title and description. Both of these values are provided in the model XML file but only one language may be supported: the language of the values specified in the XML file.

To support localization of a model, it is possible to augment the model XML values with locale-specific values. This is achieved by registering a standard Java resource bundle for each language variant of a model.

You may be asking why you need to localize content-model values. Often, it is required to render user interfaces that are driven from the content model, such as a property sheet that displays a grid of property name and value.

The content models provided out of the box are all augmented with a default (for US English) Java resource bundle. The following is an extract from the resource bundle for the ECM domain model:

cm_contentmodel.description=Alfresco Content Domain Model
cm_contentmodel.type.cm_object.title=Object
cm_contentmodel.type.cm_object.description=Base Content Domain Object
cm_contentmodel.property.cm_name.title=Name
cm_contentmodel.property.cm_name.description=Name

cm_contentmodel.type.cm_folder.title=Folder
cm_contentmodel.type.cm_folder.description=Folder
cm_contentmodel.association.cm_contains.title=Contains
cm_contentmodel.association.cm_contains.description=Contains

Resource bundles are composed of many key/value pairs. For content models, the keys are structured as follows:

<model_prefix>_<model_name>.[title|description]

or

<model_prefix>_<model_name>.<feature>.<feature_prefix>_<feature_name>.
[title|description]

Where:

  • model_prefix is the model namespace prefix.

  • model_name is the model name.

  • feature is one of type, aspect, property, association, constraint, or data type.

  • feature_prefix is the namespace prefix of the feature definition name.

  • feature_name is the feature definition name.

Content model resource bundles must be registered with their associated content model. If you remember back to how to register a content model, there are two approaches: bootstrap and dynamic. The same applies for content model resource bundles.

In the bootstrap case, the Dictionary Bootstrap component supports the additional labels property, which allows for a list of resource bundle files (located in the classpath) to be specified. The Alfresco content repository must be restarted in order for the resource bundle to be registered.

<bean id="kbmodel.extension.dictionaryBootstrap" parent="dictionaryModelBootstrap"
    depends-on="dictionaryBootstrap">
  ...
  <property name="labels">
    <list>
      <value>alfresco/extension/kbModel.properties</value>
    </list>
  </property>
  ...
</bean>

In the dynamic case, the resource bundle file is placed into the content repository folder

Company Home/Data Dictionary/Messages

Again, Alfresco Explorer is the best tool for uploading and editing resource bundles in this folder.

THE RELATIONSHIP TO CMIS

CMIS defines a data model, which encapsulates the core concepts found in most content repositories. Alfresco provides an implementation of the CMIS bindings, and as part of the implementation maps the Alfresco content metamodel to the CMIS domain model. This allows content models defined in Alfresco to be exposed and manipulated via CMIS.

The CMIS data model (as shown in Figure 5-5) is summarized as follows:

FIGURE 5-5

Figure 5.5. FIGURE 5-5

The core of the domain model allows for the definition of object types with associated properties. Types are identified by their type ID and may inherit their definition from a parent type. Features of a type include whether they can be queried by the CMIS query language, filed into multiple folders, and controlled via permissions. Features of a property include its data type, whether a value is required, and a default value if one is not explicitly provided.

You may be thinking that these features are familiar, and the Alfresco content modeling capabilities described in this chapter are indeed very similar. This is a good thing and makes it possible to map between the CMIS data model and the Alfresco content metamodel with little loss of information.

The Alfresco content metamodel is mapped to the CMIS data model as follows:

  • Type — Maps to CMIS Object Type

  • Property — Maps to CMIS Property Definition

  • Peer Association — Maps to CMIS Relationship

You may be wondering how child associations are mapped. The obvious approach is to also map them to CMIS Relationship, but CMIS has special built-in support for hierarchies through CMIS Folder and CMIS Document. So, Alfresco maps its out-of-the-box types cm:folder and cm:content (as defined in the Alfresco ECM domain model) to CMIS Folder and CMIS Document, respectively. A folder may contain a mixture of documents and folders, allowing for a hierarchy of documents to be built. Through this, CMIS supports an implicit notion of parent to child, to which Alfresco maps its child association. Subtypes of cm:folder and cm:content are exposed as subtypes of CMIS Folder and Document, respectively.

CMIS does not explicitly support the notion of an aspect. However, it does provide a CMIS Policy, which represents an administrative policy that can be enforced by the content repository. Alfresco maps each aspect defined in a content model to a CMIS Policy.

Services offered by CMIS allow the discovery of type definitions and the ability to create objects of a given type, similar to those of the Alfresco JavaScript API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.108.105