You are on page 1of 69

Alfresco Content Services 6.

Developer Guide
Contents

Contents

Developer Guide........................................................................................3
Alfresco Content Services architecture............................................................................... 3
Guiding design principles............................................................................................ 4
System architecture..................................................................................................... 5
Deployment architecture............................................................................................ 56
Deployment options................................................................................................... 58
Programming models.................................................................................................59
Repository concepts.................................................................................................. 60
Mini glossary.............................................................................................................. 65

Developer Guide 2
Developer Guide

Developer Guide
The Developer Guide includes extensive guidance and reference materials to aid the developer in
creating applications and extensions for Alfresco.
This Developer Guide PDF does not include the full developer documentation and
API references. To access the complete set of documentation, see the online Alfresco
documentation.
There are a number of approaches to developing for Alfresco depending on what you want to
do. For example, if you are writing a client application, perhaps in Ruby or Python to connect to
Alfresco either on-premise, or Alfresco in the Cloud, then you would most likely use the Alfresco
REST API. If on the other hand you wanted to write a server-side extension in Java, you would
use the Public Java API, or perhaps write a web script using Java, JavaScript and FreeMarker.
Generally if you are creating extensions to Alfresco you would use the Alfresco SDK. This allows
you to work in your IDE of choice, using technologies you already know, such as Java and
Maven.
This Developer guide attempts to lay out the various options available to you, so you can use the
right approach, depending on what you want to achieve.

Alfresco Content Services architecture


This gives an introduction to the Alfresco Content Services architecture from a developer's
perspective. At its core is a repository that provides a store for content, and a wide range of
services that can be used by content applications to manipulate the content.
The following diagram illustrates the three main components that the Alfresco Content Services
consits of. The Platform, the User Interface (UI), and the Search engine. These components are
implemented as separate web applications:

The main component is called the Platform and is implemented in the alfresco.war web
application. It provides the repository where content is stored plus all the associated content
services. Alfresco Share provides a web client interface (that is a User Interface, UI) for the
repository and is implemented as the share.war web application. Share makes it easy for users
to manage their sites, documents, users and so on. The search functionality is implemented on
top of Apache Solr 6 and provides the indexing of all content, which enables powerful search
functionality. There are also mobile clients that will access the content via ReST APIs provided by
the platform.
Most Alfresco Content Services projects will implement a domain specific web client based on the
Alfresco Application Development Framework (ADF). It gives you full freedom to design a content
and process web client supporting exactly the use cases needed by the domain. Giving the end-
users the best possible experience.
The Platform and UI components run in the same Apache Tomcat web application server. The
Search component runs in its own Jetty web application server. The Platform is usually also
integrated with a Directory Server (LDAP) to be able to sync users and groups with Alfresco
Content Services. And most installations also integrates with an SMTP server so the Platform can
send emails, such as site invitations.
For more information about the internals of the Platform, and specifically the content repository,
see the concepts section.
The Platform also contains numerous APIs, Services, and Protocols.

Developer Guide 3
Developer Guide

Alfresco provides a number of extension points to allow you to customize Alfresco Content
Services. These extensions points have various formats, but include:

• Platform extension points and detailed architecture


• Share extension points and detailed architecture
• Application integration points and detailed architecture
• APIs
• Protocols
• Services

The links in the list above provide further information on each of these topics.

Guiding design principles


The Alfresco Content Services architecture supports the requirements of Enterprise Content
Management (ECM) applications, such as Document Management (DM), Web Content
Management (WCM), Records Management (RM), Digital Asset Management (DAM), and
Search.

Support ECM requirements


Each of these disciplines has unique and overlapping characteristics so that the design of each
capability is not done in isolation but in the context of the whole system.

Simple, simple, simple


Alfresco Content Services aims to be as simple as possible to develop against, customize,
deploy, and use. The simplest and probably most widely deployed ECM solution is the shared
document drive: the architecture is driven by the aim to be as simple as a shared drive.

Scaling to the enterprise


Every service and feature is designed up front to scale in terms of size of data set, processing
power, and number of users.

Modular approach
Alfresco Content Services architecture takes a modular approach in which capabilities are
bundled into modules whose implementation can be replaced if required, or not included at all.
Aspect-Oriented Programming (AOP) techniques allow for fine-tuning and optimization of an ECM
solution.

Incorporating best-of-breed libraries


Where possible, Alfresco Content Services incorporates best-of-breed third-party libraries. The
open source nature lends itself to integrating with the wealth of available open source libraries.
This is done whenever it is more profitable to integrate than build or whenever expertise is better
provided in another project rather than in-house.

Environment independence
Alfresco Content Services does not dictate the environment upon which it depends, allowing
choice in the operating system, database, application server, browser, and authentication system
to use when deploying. ECM is less about the application and more about the services embedded
within an application. You can choose how to package Alfresco Content Services — for example,
as a web application, an embedded library, or portlet.

Developer Guide 4
Developer Guide

Solid core
The heart of Alfresco Content Services is implemented in Java. This decision was driven by
the wealth of available Java libraries, monitoring tools, and enterprise integrations. Java is
also a trusted runtime for many enterprises wishing to deploy applications in their data centers.
Each capability is implemented as a black-box Java service tested independently and tuned
appropriately.

Scriptable extensions
Extensions will always need to be created for custom solutions and there are many custom
solutions versus the single Alfresco Content Services core. Therefore, extension points are
developed using JVM-based scripting languages, allowing a much wider pool of developers to
build extensions versus those that can contribute to the core. Extensions are packaged entities,
allowing for the growth of a library of third-party reusable extensions.

Standards-based approach
The architecture always complies with standards where applicable and advantageous. Primary
concerns are to reduce lock-in, improve integration possibilities, and hook into the ecosystems
built around the chosen standards.

Architecture of participation
The architecture promotes a system designed for community contribution. In particular,
the architecture principles of a solid core, modularity, standards compliance, simplicity of
development, and scriptable extensions encourage contribution of plug-ins and custom ECM
solutions. Participation complements the open source approach to the development of Alfresco
Content Services and fosters growth of the Alfresco community. As the community grows,
the quality of self service improves, as well as the quality of feedback. This, in turn, enhances
Alfresco Content Services and creates the ultimate feedback loop.

System architecture
At the core of the Alfresco Content Services system is a repository supported by a server that
persists content, metadata, associations, and full text indexes. Programming interfaces support
multiple languages and protocols upon which developers can create custom applications and
solutions. Out-of-the-box applications provide standard solutions such as document management
and records management.

The Alfresco Content Services system is implemented in Java, which means that it can run
on most servers that can run the Java Standard Edition. The platform components have been
implemented using the Spring framework, which provides the ability to modularize functionality,
such as versioning, security, and rules. The platform provides a scripting environment to simplify
adding new functionality and developing new programming interfaces. This portion of the
architecture is known as Web Scripts and can be used for both data and presentation services.
The lightweight architecture is easy to download, install, and deploy.

Ultimately, Alfresco Content Services is used to implement ECM solutions, such as document
management and records management. There can also be elements of collaboration and search
across these solutions.

A content management solution is typically divided into clients and a server. The clients offer
users a user interface to the solution and the server provides content management services and
storage. Solutions commonly offer multiple clients against a shared server, where each client is
tailored for the environment in which it is used.

Developer Guide 5
Developer Guide

Clients
Alfresco Content Services offers a web-based client called Alfresco Share, built entirely with
the web script technology. Share provides content management capabilities with simple user
interfaces, tools to search and browse the repository, content such as thumbnails and associated
metadata, previews, and a set of collaboration tools such as wikis and discussions. Share is
organized as a set of sites that can be used as a meeting place for collaboration. It's a web-
based application that can be run on a different server to the server that runs the platform with
repository, providing opportunities to increase scale and performance.

Alfresco has offered the Share web client for a long time. However, if a content management
solution requires extensive customization to the user interface, which most do, then it is not
recommended to customize Share. Develop instead a custom client with the Alfresco Application
Development Framework (ADF), which is Angular based.

Clients also exist for mobile platforms, Microsoft Outlook, Microsoft Office, and the desktop. In
addition, users can share documents through a network drive via WebDAV.

Server
The content application server comprises a content repository and value-added services for
building solutions.

The content application server provides the following categories of services built upon the content
repository:

• Content services (node management, transformation, tagging, metadata extraction)


• Control services (workflow, records management, change sets)
• Collaboration services (calendar, activities, wiki)

Clients communicate with the content application server and its services through numerous
supported protocols. HTTP and SOAP offer programmatic access while FTP, WebDAV, IMAP,
and Microsoft SharePoint protocols offer application access.

Platform
The platform architecture consists of the repository and related services. The platform contains
the key extension points for building your own extensions.
This section covers the platform architecture in detail. It also goes through Content Modeling,
which is a fundamental part of setting up a content management system. Finally it covers the
platform access protocols and the platform modularity.

Platform architecture
The platform architecture consists of the repository and related services. The platform contains
the key extension points for building your own extensions.
The following diagram illustrates the platform architecture and extension points. Note that this
does not represent a complete list of extension points:

Developer Guide 6
Developer Guide

The platform consists of the repository and all services, developer extension points, and APIs.
The repository provides storage for documents and other content. The content metadata is
stored in a relational database, while the content itself is stored directly on the file system. The
relationships between content items, and their various properties (metadata) are defined in one or
more content models.
Content models can be thought of as describing types of content and the relationships between
pieces of content. For example, there is a relationship between a content that has a container
functionality (that is, folder), and the piece of content contained within it (that is, sub-folders and
files). There might be constraints defined in the content model, such as a content type cannot
contain other content unless it is a container type.
As well as the basic content storage functionality, the platform provides a wide range of content-
related services. These include core services such as the Node Service, and the Version Service.
There are also higher-level services such as Thumbnail Service (for creating thumbnail images
and renditions of documents), the Site Service used for creating and managing sites in the Share
application, and the Tagging Service, which provides the ability to tag content with keywords. The
following sections of this documentation provide a brief tour of the available services.
Typically these services are implemented in Java, and expose an API described by the Public
Java API.
The platform is highly extensible. You can write extensions in Java, JavaScript, and FreeMarker,
and you can write client applications in any language using the . You can create new content
models that define new content types, metadata, and relationships. You can define custom
actions that the repository will carry out when certain events happen (such as when new content
is added to the repository). You can even create entirely new services, if required.
When you need to create custom business workflow you should use the Alfresco Process
Services (APS) product.

Content modeling
Content modeling is a fundamental building block of the repository that provides a foundation for
structuring and working with content.
For more information about working with custom metadata models (aspects, types and
forms), flexible content organization and actions in the Model Manager App, see Content
modeling.

Developer Guide 7
Developer Guide

Content modeling specifies how nodes stored in the repository are constrained, imposing a
formal structure on nodes that an application can understand and enforce. Nodes can represent
anything stored in the repository, such as folders, documents, XML fragments, renditions,
collaboration sites, and people. Each node has a unique ID and is a container for any number of
named properties, where property values can be of any data type, single or multi-valued.
Nodes are related to each other through relationships. A parent/child relationship represents a
hierarchy of nodes where child nodes cannot outlive their parent. You can also create arbitrary
relationships between nodes and define different types of nodes and relationships.
A content model defines how a node in the repository is constrained. Each model defines one
or more types, where a type enumerates the properties and relationships that a node of that
type can support. Often, concepts that cross multiple types of node must be modeled, which the
repository supports through aspects. Although a node can only be of a single type, you can apply
any number of aspects to a node. An aspect can encapsulate both data and process, providing a
flexible tool for modeling content.
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 (or modeling) of entities within the domain. For example,
many applications are built around the notion of folders and documents. It is content modeling
that adds meaning to the node data structure.

The repository provides services for reading, querying, and maintaining nodes. Events are fired
on changes, allowing for processes to be triggered. In particular, the repository provides the
following capabilities based on events:
• Policies: event handlers registered for specific kinds of node events for either all nodes or
nodes of a specific type
• Rules: declarative definition of processes based on addition, update, or removal of nodes
(for example, the equivalent of email rules)

Models also define kinds of relationships, property data types, and value constraints. A special
data type called content allows a property to hold arbitrary length binary data. Alfresco Content
Services comes prepackaged with several content models. You can define new models for
specific use cases from scratch or by inheriting definitions from existing models.

Developer Guide 8
Developer Guide

For more information see content model introduction.

Access protocols
Alfresco Content Services supports a number of different protocols for accessing the content
repository. Their availability extends the options available to developers, when building their own
applications and extensions.
Protocols provide developers with another possible avenue for building their own applications
and extensions. For example, if you are building a client application to connect with multiple
repositories from multiple vendors, including Alfresco Content Services, then CMIS is a
consideration. If you are building a client to connect via the SharePoint Protocol, then use the
Alfresco Office Services (AOS). Protocols provide a resource for developers, in addition to the
numerous other extension points and APIs built into Alfresco.
When any of these protocols are used to access or upload content to the repository, access
control is always enforced based on configured permissions, regardless of what protocol that is
used.
The following table list some of the main protocols supported by Alfresco Content Services and
links to more detailed documentation.

Protocol Description Support Status

HTTP The main protocol used to Standard in Alfresco Content


access the repository via for Services and Community
example the ReST APIs. Edition.

WebDAV Web-based Distributed Standard in Alfresco Content


Authoring and Versioning is a Servicesand Community Edition.
set of HTTP extensions that lets
you manage files collaboratively
on web servers. It has strong
support for authoring scenarios
such as locking, metadata,
and versioning. Many content
production tools, such as the
Microsoft Office suite, support
WebDAV. Additionally, there are
tools for mounting a WebDAV
server as a network drive.

FTP File Transfer Protocol - standard Standard in Alfresco Content


network protocol for file upload, Services and Community.
download and manipulation.
Useful for bulk uploads and
downloads.

Alfresco Office Services Alfresco Office Services (AOS) Standard in Alfresco Content
allow you to access Alfresco Services and Community
Content Services directly Edition.
from all your Microsoft Office
applications.

CMIS Alfresco fully implements both Standard in Alfresco Content


the CMIS 1.0 and 1.1 standards Services and Community
to allow your application to Edition.
manage content and metadata in
an on-premise repository.

Developer Guide 9
Developer Guide

Protocol Description Support Status

IMAP Internet Message Access Standard in Alfresco Content


Protocol - allows access to Services and Community
email on a remote server. Edition.
Alfresco Content Services
can present itself as an email
server, allowing clients such as
Microsoft Outlook, Thunderbird,
Apple Mail and other email
clients to access the content
repository, and manipulate
folders and files contained there.
IMAP supports three modes of
operation:
1. Archive: allows email
storage in the repository
by using drag/drop and
copy/paste from the IMAP
client
2. Virtual: folders and files
held in the repository are
exposed as emails within
the IMAP client with the
ability to view metadata
and trigger actions using
links embedded in the
email body
3. Mixed: a combination of
both archive and virtual

SMTP It is possible to email Standard in Alfresco Content


content into the repository Services and Community
(InboundSMTP). A folder can be Edition.
dedicated as an email target.

All the protocol bindings expose folders and documents held in the repository. This means a
client tool accessing the repository using the protocol can navigate through folders, examine
properties, and read content. Most protocols also permit updates, allowing a client tool to modify
the folder structure, create and update documents, and write content. Some protocols also allow
interaction with capabilities such as version histories, search, and tasks.
Internally, the protocol bindings interact with the repository services, which encapsulate the
behavior of working with folders and files. This ensures a consistent view and update approach
across all client tools interacting with the content application server.
A subsystem for file servers allows configuration and lifecycle management for each of the
protocols either through property files or JMX.

Modularity
The Alfresco Content Services system is modular. Every moving part is encapsulated as a
service, where each service provides an external face in a formally defined interface and has one
or more black-box implementations.
The system is designed this way to allow for:
• Pick and mix of services for building an ECM solution

Developer Guide 10
Developer Guide

• Reimplementation of individual services


• Multiple implementations of a service, where the appropriate implementation is chosen
based on the context within which the solution is executed
• A pattern for extending Alfresco Content Services (at design and runtime)
• Easier testing of services

To support this approach, Alfresco Content Services used the Spring framework for its factory,
Dependency Injection, and Aspect-Oriented Programming (AOP) capabilities. Services are bound
together through their interfaces and configured using Spring’s declarative Dependency Injection.

A service interface is defined as a Java interface. For services that form the internal embedded
API for extensions, cross-cutting concerns such as transaction demarcation, access control,
auditing, logging, and multi-tenancy are plugged in through Spring AOP behind the service
interface. This means that service implementations are not polluted with these concerns. It
also means the cross-cutting concerns can be configured independently or even switched off
across the server if, for example, performance is the top-most requirement and the feature is not
necessary.

Multiple services are aggregated into an Alfresco Content Services subsystem where a
subsystem represents a complete coherent capability of the Alfresco Content Services server,
such as authentication, transformation, and protocols. As a unit, subsystems have their own
lifecycle where they can be shut down and restarted while the server is running. This is useful
to disable aspects of the server, or reconfigure parts of it, such as how LDAP synchronization
is mapped. Each subsystem supports its own administration interface that is accessible through
property files or JMX.

Web UI
The Web UI architecture consists of a number of web clients and the Application Development
Framework (ADF).
This section covers the Web UI architecture in detail. There are a number of web clients available
when accessing the repository. There is also the Application Development Framework that can
be used to build domain specific web applications.

Application development architecture


This section gives an overview of the Alfresco application development architecture. It covers the
Application Development Framework (ADF) and the Alfresco JavaScript framework.
Alfresco has traditionally always offered a Web client called Share, which is still available.
However, if a content management solution requires extensive customization to its user interface,
which most do, then it is not recommended to customize Share. Develop instead a custom client
with the Alfresco Application Development Framework, which is Angular based.

Developer Guide 11
Developer Guide

Application Development Framework (ADF)


This page gives an introduction to the Alfresco Application Development Framework (ADF), which
is used to build custom domain specific Web UIs.
Overview
The Alfresco Application Development Framework, referred to as ADF, is built on top of the
Angular 5 JavaScript framework. You can think of ADF as a library of Alfresco web components
that can be used to build a content management web application and/or a process management
web application.
There are a number of web components that you can use to integrate your web application with
Alfresco Content Services (ACS). Here are some of these components:
• Folder Hierarchy Breadcrumbs - display a breadcrumb with clickable folder path
• Document List - list folders and files
• Search - search folders and files
• Tag - manage and list tags
• Upload - upload files via button or drag-and-drop
• Viewer - preview files in the browser
• Webscript - call a Web Script (i.e. a ReST call)
And for integrating with Alfresco Process Services (APS) you have the following components:
• Analytics - display graph reports such as process instance overview
• Diagram - show process definition diagram, if associated with running process instance
then the activities are highlighted according to their state
• Process Apps - shows a list of process applications
• Process List - show a list of process instances
• Process Details - multiple components are available to show different details for a
process
• Task List - show a list of task instances for a process instance(s)
• Task Details - multiple components are available to show different details for a task
There are also a number of generic components that are used with both ACS and APS:
• Breadcrumbs - indicates the current position within a navigation hierarchy
• Toolbar - an extension to the Angular Material toolbar with a title and color
• Accordion - creates a collapsable accordion menu
• Card View - displays properties in a nice layout
• Data Table - generic data table implementation that is used by, for example, Document
List
• Drag-and-Drop - Drag and drop files into for example a folder
• Form - display properties from nodes, tasks, and other sources in a form defined in JSON
• Login - authenticates with both services
• User Info - display information about a user
For a complete list of all components with documentation see the ADF Component Catalogue.
Architecture
These ADF components don’t talk directly to the ACS and APS backend services. There are
some layers between them that are worth knowing about before you start coding. The ADF

Developer Guide 12
Developer Guide

components talk to ADF services, which in turn talks to the Alfresco JS API, which internally calls
ACS and APS via their respective ReST APIs. You could use the both the ADF services and the
Alfresco JS API directly from your application if there is no ADF component available to do what
you want. In fact, you will quite frequently have to use the ADF services in your application to
fetch content nodes, process instances, task instances etc.
The following picture illustrates the architecture of an ADF solution:

The ADF components and services are implemented in Angular, which in turn is implemented in
TypeScript. The Alfresco JavaScript library is pure JavaScript and could be used with any other
JavaScript framework.
Application Generator
There is an ADF application generator that can be very useful if you just want to quickly get going
with an ADF project, such as for a demo or proof-of-concept scenario. It covers use cases for
both ACS and APS. It can be used to generate the following types of ADF applications:
• ADF Content Management App (use this template if your app is only going to talk to ACS)
• ADF Process Management App (use this template if your app is only going to talk to APS)
• ADF Content and Process Management App
Using the App Generator is simple. Install the Yeoman tool. Then install the App Generator as
follows:

$ sudo npm install generator-alfresco-adf-app -g


Password:
+ generator-alfresco-adf-app@2.3.0
added 243 packages in 5.438s

Running the generator is easy:

$ yo
? 'Allo Martin! What would you like to do? (Use arrow keys)
Run a generator

Developer Guide 13
Developer Guide

# Alfresco Adf App


##############
Update your generators
Install a generator
Find some help
Clear global config

Select the 'Alfresco Adf App' generator and follow instructions.

Alfresco JavaScript API


This page gives an introduction to the Alfresco JavaScript API, which can be used from any third
party JavaScript library.
The Alfresco JavaScript API is not normally used directly. Instead the Alfresco Application
Developement Framework (ADF) is used, which uses the JavaScript API indirectly. But there
are situations when it might be necessary to use the JavaScript API directly, such as when ADF
cannot be used. ADF is based on Angular and if another JavaScript library such as React has
been adopted, then it is beneficial to use the Alfresco JavaScript API directly from React.
The Alfresco JavaScript library abstracts the Alfresco Content Services (ACS) ReST API and the
Alfresco Process Services (APS) ReST API, so a lot of work has been done to make it smooth to
use the Alfresco ReST APIs from a third party JavaScript library. For example, authentication with
both ACS and APS is handled automatically by the Alfresco JavaScript library.
For more information about the Alfresco JavaScript API, and examples of how to use it, have a
look here.

Share architecture
When developing for Share it is important to understand the application architecture and the
underlying development frameworks. It is also important to know what extension points that are
available to you for customizing the UI in a supported way.

Introduction
Alfresco Share (share.war) is a web application that runs on the Java Platform. In a
development environment it is usually deployed and run on top of Apache Tomcat. Share is built
up of a main menu that leads to pages, which is similar to most other web applications that you
might come across. However, there is one special page type called Dashboard that contains
dashlets. A Dashboard page can be configured by the end-user, who can add, remove, and
organize the dashlets on the page.
Share pages and dashlets are implemented with something called web scripts, which is basically
REST-based APIs. These APIs are called Surf web scripts when you are dealing with Alfresco
Share. There is also repository web scripts that are used to extend the repository web application
(alfresco.war) with REST-based APIs. Surf Web Scripts are referred to as Presentation Web
Scripts and the repository web scripts as Data Web Scripts.
Share web scripts, pages, and dashlets are implemented with a user interface (UI) development
framework called Surf. This framework was originally developed by Alfresco, then donated to the
Spring Source foundation, and finally brought back into Alfresco products. It provides a way of
breaking a HTML page into re-usable component parts. Surf is built on top of the Spring Web
MVC technology, which in turn uses the Spring Framework.
Developers can also add completely new pages and dashlets to the Share UI when content
should be viewed or handled in a specific way. Sometimes it is also required to modify existing
pages. To customize the Share UI developers use so called Extension Points, which are
supported ways of injecting new custom code that should alter the functionality of the Share web
application.

Developer Guide 14
Developer Guide

The following picture gives an overview of the Alfresco Share application architecture, note that
not all available extension points are illustrated in this picture:

Share gets the content that it should display in pages and dashlets by calling repository
web scripts, which returns JSON or XML that can be incorporated into the presentation. The
presentation is actually put together with two different kinds of JavaScript frameworks, Yahoo
UI library (YUI) and Aikau, which is based on Dojo. An Aikau page is based on Surf but it makes
page composition much easier than with pure Surf pages.
You can focus solely on Aikau if the only thing you are going to do is add new stuff to the Share
UI. However, if you need to alter behavior of existing pages, then you might also need to get up to
speed on the details of the Surf page model, as only the following has been converted to Aikau:
• Share Header Menu and Title (4.2)
• Live Search (5.0)
• Filtered Search Page (5.0)
• Search Management Page (5.0)
• Site Management Page (5.0)
• Analytics and Reporting Widgets (5.0)
• Document List prototype (5.0)

The following sections get into a bit more details around Surf pages and Aikau pages.

Server Side Framework (Surf)


The layout of a Share page is defined with the Surf development framework, which is a server
side framework (Surf deep dive). This means that the involved files are processed on the
server side (compared to Browser processing of JavaScript files). Surf is based on the Model
View Controller (MVC) pattern where the controller(s) is mostly implemented in server side
JavaScript (The Rhino JavaScript engine is included on the server side). The template is written
in FreeMarker, and the model is a hash map that is set up in the controller(s) and available in the
template.
Each page template defines one or more regions for things like header, footer, body, navigation,
see the following picture:

Developer Guide 15
Developer Guide

To be able to reuse regions we can scope them to page, template, or global usage:

Each region is implemented as a reusable component. A component implementation is done with


a Surf web script, which is the same thing as the REST-based request and response model, the
predominant Web Service design model. The component web scripts will typically return HTML
fragments that make up different parts of the page:

Developer Guide 16
Developer Guide

With all these different objects we might expect there to be some form of model that makes up
the whole Surf UI development framework. It looks like this:

The model is referred to as the siteData and has more stuff than just pages and templates
(siteData Reference). You will however mostly be dealing with component, page, and template-
instance files, which are simple XML files:

/WEB-INF/classes/alfresco
/site-data
/chrome
/components
...
global.header.xml
/component-types
/configurations
/content-associations
/extensions
/page-associations
/pages
...
documentlibrary.xml

Developer Guide 17
Developer Guide

...
search.xml
...
task-details.xml
...
/page-types
/template-instances
1-column.xml
2-columns.xml
3-columns.xml
...
content-viewer.xml
...
search.xml
...
/template-types
/themes

The Site Data model defines the page in XML, like in the following example for Search
(alfresco/tomcat/webapps/share/WEB-INF/classes/alfresco/site-data/pages/
search.xml):

<?xml version='1.0' encoding='UTF-8'?>


<page>
<title>Search</title>
<title-id>page.search.title</title-id>
<description>Search view</description>
<description-id>page.search.description</description-id>
<template-instance>search</template-instance>
<authentication>user</authentication>
<components>

<!-- Title -->


<component>
<region-id>title</region-id>
<url>/components/title/search-title</url>
</component>

<!-- Search -->


<component>
<region-id>search</region-id>
<url>/components/search/search</url>
</component>

</components>
</page>

Here we can see that some components have been defined inline in the search page definition,
instead of in the /components directory as separate files. The name of the page definition file is
implicitly setting the page id to search. A corresponding template instance file is expected to be
present in the template-instances directory. In our case it will be a file called search.xml:

<?xml version='1.0' encoding='UTF-8'?>


<template-instance>
<template-type>org/alfresco/search</template-type>
</template-instance>

It will have a link to the physical template that contains the layout of the page. The template files
are located under a different directory called /templates, which is on the same level as the
site-data directory:

Developer Guide 18
Developer Guide

/WEB-INF/classes/alfresco
/site-data
/templates
/org
/alfresco
1-column.ftl
2-columns.ftl
3-columns.ftl
...
content-viewer.ftl
...
search.ftl
...

The search.ftl template file looks like this with the regions etc:

<#include "include/alfresco-template.ftl" />


<@templateHeader />

<@templateBody>
<@markup id="alf-hd">
<div id="alf-hd">
<@region scope="global" id="share-header" chromeless="true"/>
</div>
</@>
<@markup id="bd">
<div id="bd">
<div class="yui-t1">
<div id="yui-main">
<@region id="search" scope="page" />
</div>
</div>
</div>
</@>
</@>

<@templateFooter>
<@markup id="alf-ft">
<div id="alf-ft">
<@region id="footer" scope="global" />
</div>
</@>
</@>

The search page reuses the global header and footer components and then defines a page
specific region called search. The web script to call for the search component is already defined
in the page definition XML above (that is, /components/search/search). The controller file for
the search Web Script looks like this (alfresco/tomcat/webapps/share/WEB-INF/classes/
alfresco/site-webscripts/components/search/search.get.js):

/**
* Search component GET method
*/

function main()
{
// fetch the request params required by the search component template
var siteId = (page.url.templateArgs["site"] != null) ?
page.url.templateArgs["site"] : "";
var siteTitle = null;
if (siteId.length != 0)
{
// Call the repository for the site profile

Developer Guide 19
Developer Guide

var json = remote.call("/api/sites/" + siteId);


...

This is server side JavaScript code that sets up a model with data for the template. The template
looks like this (alfresco/tomcat/webapps/share/WEB-INF/classes/alfresco/site-
webscripts/components/search/search.get.html.ftl):

<@markup id="css" >


<#-- CSS Dependencies -->
<@link href="${url.context}/res/components/search/search.css"
group="search"/>
</@>

<@markup id="js">
<#-- JavaScript Dependencies -->
<@script src="${url.context}/res/components/search/search-lib.js"
group="search"/>
<@script src="${url.context}/res/components/search/search.js"
group="search"/>
</@>

<@markup id="widgets">
<@createWidgets group="search"/>
</@>

<@markup id="html">
<@uniqueIdDiv>
<#assign el=args.htmlid>
<#assign searchconfig=config.scoped['Search']['search']>
<div id="${el}-body" class="search">
<#assign context=searchconfig.getChildValue('repository-
search')!"context">
<#if searchQuery?length == 0 && context != "always">
<div class="search-sites">
...

The template is where we will find references to client side code/resources. The css and js
sections above points to the client side CSS and JS that should be part of the <head> section in
the web page, and downloaded and executed by the browser to create the user interface. So as
we are starting to talk about the client side, let's dig into it a bit more in the next section.

Client Side Frameworks (Surf Pages and Aikau Pages)


To get an idea of the differences between the old school Surf pages, and the new Surf pages
called Aikau, this example implements a simple page in both client side frameworks. The thing
that might be a bit confusing to start with is that Aikau pages are also old school Surf pages under
the hood. An Aikau page actually uses a predefined Surf page as a starting point. Start with an
old school Hello World page and see how to add it to the Share UI.
Hello World Old School Surf Page
The following steps are needed to add a Surf Page:
• Add a Surf Page definition file (XML)
• Add a Template Instance file (XML)
• Add a physical Template file (FTL)
• Add a properties file (.properties) - Optional but good practice
• Add Web Script(s) that fetches content to display (if you have page scoped regions and
use an existing template)
The full tutorial, and introduction to Surf Pages, can be found (here).

Developer Guide 20
Developer Guide

Next, have a look at how to implement the same Hello World page with Aikau.
Hello World Aikau Page
To implement the Hello World page in Aikau we have to go through the following steps:
• Add a Web Script descriptor (XML)
• Add a Web Script template (FTL)
• Add a Web Script controller (JS) with page layout/model
• Add Widget to display content
• Choose what Surf Page you want to use as a basis (dp, hdp, rdp etc)
For a full tutorial and introduction to Aikau Pages, see (Introduction to Aikau Pages on page
26).

Extension Points in Share


As you can imagine, there are loads of extension points that you can use in the Share UI to build
a customized version of the user interface. In this article we have looked at the major ones, which
are old school Surf Pages, Aikau Surf pages, Aikau widgets, web scripts, Surf Module extensions,
and dashlets. I know we did not explicitly look at how to implement dashlets, but it is the same
thing as implementing a Web Script.
There are many more extension points though, for example the Document Library page in a site
can be extended via something called Document Library Actions. It is important to know about
these supported extension points, and follow them, as otherwise your code might not work in a
future release of Alfresco Content Services, and you might have trouble getting the support you
need.
Here is a list of each supported extension point in Share, for a comprehensive description of each
one go to the Share Extension Points section (OOTB = Out-of-the-box functionality):

Extension Point Name

Share Configuration

Form Controls

Form Filters

Form Field Validation Handlers

Evaluators

Site Presets

Share Themes

Document Library

Surf Extension Modules

Surf Web Scripts

Surf Web Script JavaScript Root Objects

Surf Pages

Surf Dashlets

Developer Guide 21
Developer Guide

Extension Point Name

Surf Widgets

Aikau Menus

Aikau Pages

Aikau Dashlets

Aikau Widgets

Modifying OOTB Surf Pages

Modifying OOTB Surf Dashlets

Modifying OOTB Surf Widgets

Modifying OOTB Aikau Pages

Modifying OOTB Aikau Dashlets

Modifying OOTB Aikau Widgets

Modifying OOTB Surf Web Scripts

Introduction to Surf Pages


Use this information for a brief introduction to Spring Surf Pages.
Let's see how we can implement a Hello World page with the old school Surf Page framework.
The following steps are needed to add a Surf Page:
• Add a Surf Page definition file (XML)
• Add a Template Instance file (XML)
• Add a physical Template file (FTL)
• Add a properties file (.properties) - Optional but good practice
• Add Web Script(s) that fetches content to display (if you have page scoped regions and
use an existing template)
Let's start out with the page definition file, create a file called helloworldhome.xml in the
alfresco/tomcat/shared/classes/alfresco/web-extension/site-data/pages directory.
You will have to create the site-data and pages directories. We are not using a build project to
be able to focus solely on Surf.
Add the following XML to the file:

<?xml version='1.0' encoding='UTF-8'?>


<page>
<title>Hello World Home</title>
<title-id>page.helloworldhome.title</title-id>
<description>Hello World Home Description</description>
<description-id>page.helloworldhome.description</description-id>
<template-instance>helloworldhome-three-column</template-instance>
<authentication>none</authentication>
</page>

Here we are defining the title and description of the page both hard-coded in the definition, and
as references to a properties file with labels (i.e. the title-id and description-id elements).

Developer Guide 22
Developer Guide

The page will not require any authentication, which means we cannot fetch any content from the
Alfresco Repository from it. It is also going to use a three column template, or that is the idea, you
can name the template instance whatever you want.
Now create the template instance file called helloworldhome-three-column.xml in the
alfresco/tomcat/shared/classes/alfresco/web-extension/site-data/template-
instances directory. You will have to create the template-instances directory:

<?xml version='1.0' encoding='UTF-8'?>


<template-instance>
<template-type>org/alfresco/demo/helloworldhome</template-type>
</template-instance>

This file just points to where the FreeMarker template for this page will be stored. So create the
alfresco/tomcat/shared/classes/alfresco/web-extension/templates/org/alfresco/
demo directory path. Then add the helloworldhome.ftl template file to it:

This is just a test page. Hello World!

Continue with the properties file for the page title and description. Create a file called
helloworldhome.properties in the alfresco/tomcat/shared/classes/alfresco/web-
extension/messages directory. You will have to create the messages directory:

page.helloworldhome.title=Hello World
page.helloworldhome.description=Hello World Home Description

This file just points to where the FreeMarker template for this page will be stored. We also
need to tell Alfresco Share about the new resource file, rename the custom-slingshot-
application-context.xml.sample to custom-slingshot-application-context.xml, it is
located in the web-extension directory. Then define the following bean:

<bean id="org.alfresco.demo.resources"
class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<value>alfresco.web-extension.messages.helloworldhome</value>
</list>
</property>
</bean>

To test this page you will have to restart Alfresco. It can then be accessed via the http://
localhost:8080/share/page/helloworldhome. The page does not look very exciting:

So we are missing both the Share header and footer, which turns out to be global components
that we can easily include. We just need to change the template file a bit. Open up the
helloworldhome.ftl file and change it so it looks like this:

<#include "/org/alfresco/include/alfresco-template.ftl" />


<@templateHeader></@>
<@templateBody>
<@markup id="alf-hd">
<div id="alf-hd">
<@region scope="global" id="share-header" chromeless="true"/>
</div>
</@>

Developer Guide 23
Developer Guide

<@markup id="bd">
<div id="bd">
<h1>This is just a test page. Hello World!</h1>
</div>
</@>
</@>
<@templateFooter>
<@markup id="alf-ft">
<div id="alf-ft">
<@region id="footer" scope="global" />
</div>
</@>
</@>

What we are doing here is first bringing in another FreeMarker file called alfresco-
template.ftl that contains, you guessed it, FreeMarker template macros. We then use these
macros (elements starting with @) to set up the layout of the page with header and footer. The
header and footer content is fetched via the share-header and footer global scope components
(Web Scripts). To view the result of our change we need to restart the server again, after this we
should see the following:

So that looks a bit better. The next thing we want to do is to make the page a bit more dynamic,
currently we have hard-coded the content for the page in the template. Let's add a Web Script
that will return the content to display. This will require us to update the template with an extra
region as follows:

<#include "/org/alfresco/include/alfresco-template.ftl" />


<@templateHeader></@>
<@templateBody>
<@markup id="alf-hd">
<div id="alf-hd">
<@region scope="global" id="share-header" chromeless="true"/>
</div>
</@>
<@markup id="bd">
<div id="bd">
<@region id="body" scope="page" />
</div>
</@>
</@>
<@templateFooter>
<@markup id="alf-ft">
<div id="alf-ft">
<@region id="footer" scope="global" />
</div>
</@>
</@>

We have called the new region body and set page scope for it. This requires us to define a new
component for this region. This can be done either in the page XML, or as a separate file in the

Developer Guide 24
Developer Guide

site-data/components directory, we will do the latter. Create the components directory and add
a file called page.body.helloworldhome.xml to it:

<?xml version='1.0' encoding='UTF-8'?>


<component>
<scope>page</scope>
<region-id>body</region-id>
<source-id>helloworldhome</source-id>
<url>/components/helloworld/body</url>
</component>

The component file names follow a naming convention: global | template |


page>.<region-id>.[<template-instance-id | page-id>].xml The URL for this
component points to a Web Script that will return the Hello World message. Start implementing
it by creating a descriptor file called helloworld-body.get.desc.xml located in the alfresco/
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/demo
directory:

<webscript>
<shortname>helloworldbody</shortname>
<description>Returns the body content for the Hello World page.</
description>
<url>/components/helloworld/body</url>
</webscript>

Note that the URL is the same as we set in the component definition. Now implement the
controller for the Web Script, create a file called helloworld-body.get.js in the same place as
the descriptor:

model.body = "This is just a test page. Hello World! (Web Scripting)";

The controller just sets up one field in the model with the Hello World message. Now implement
the template for the Web Script, create a file called helloworld-body.get.html.ftl in the
same place as the descriptor:

<h1>${body}</h1>

Restart the server. Then access the page again, you should see the Hello World message
change to "This is just a test page. Hello World! (Web Scripting)".

To summarize a bit, the following is a picture of all the files that were involved in creating this Surf
page the old school way:

Developer Guide 25
Developer Guide

What you could do now is extend the Hello World page with some more sophisticated
presentation using the YUI library. If you do that you end up with the pattern for how most of the
old school Share pages have been implemented.
Next we will have a look at how to implement the same Hello World page the new way with Aikau.
Back to Share Architecture and Extension Points.

Introduction to Aikau Pages


Use this information for a brief overview of Aikau Pages.
Let's see how we can implement a Hello World page with the new Aikau framework.
The following steps are needed to add an Aikau Page:
• Add a Web Script descriptor (XML)
• Add a Web Script template (FTL)
• Add a Web Script controller (JS) with page layout/model
• Add Widget to display content
• Choose what Surf Page you want to use as a basis (dp, hdp, rdp etc)
OK, this might be a bit confusing, starting with a web script and then selecting a Surf page? If
you have read through the Share architecture on page 14 page then you will remember that
an Aikau page is based on a predefined Surf Page. So when you implement an Aikau page
you are actually bypassing all the Site Data model stuff, and you go directly to the Web Script
implementation that does the real job of fetching content and defining the presentation.
Start implementing the Aikau Page web script by creating a descriptor file called helloworld-
aikau.get.desc.xml located in the alfresco/tomcat/shared/classes/alfresco/web-
extension/site-webscripts/org/alfresco/demo directory:

<webscript>

Developer Guide 26
Developer Guide

<shortname>Hello World</shortname>
<description>Hello World page definition</description>
<family>Share</family>
<url>/helloworld</url>
</webscript>

Now implement the controller for the Web Script, create a file called helloworld-aikau.get.js
in the same place as the descriptor:

model.jsonModel = {
widgets: [
{
id: "SET_PAGE_TITLE",
name: "alfresco/header/SetTitle",
config: {
title: "Hello World"
}
},
{
id: "DEMO_SIMPLE_MSG",
name: "example/widgets/HelloWorldTextWidget"
}
]
};

The controller is where the main work is done when it comes to implementing the layout of the
page. If you do not need any custom widgets then it might even be the only major thing you need
to implement to get the Aikau page up and running. Now implement the template for the web
script, create a file called helloworld-aikau.get.html.ftl in the same place as the descriptor:

<@processJsonModel />

The template just kicks off the processJsonModel FreeMarker template macro, which will, as it
says, process the JSON model and assemble the page components.
Our page model contains an example widget that we need to implement. It is specified to be at
the example/widgets package path. Dojo is the JavaScript framework used behind the scenes,
and we need to tell it about the new package path. This can be done via a Spring Surf Module
extension. Create a file called example-widgets.xml and put it in the alfresco/tomcat/
shared/classes/alfresco/web-extension/site-data/extensions directory:

<extension>
<modules>
<module>
<id>Example Aikau Widgets</id>
<version>1.0</version>
<auto-deploy>true</auto-deploy>
<configurations>
<config evaluator="string-compare" condition="WebFramework"
replace="false">
<web-framework>
<dojo-pages>
<packages>
<package name="example" location="js/example"/>
</packages>
</dojo-pages>
</web-framework>
</config>
</configurations>
</module>
</modules>
</extension>

Developer Guide 27
Developer Guide

Now we can start implementing the Aikau Widget that should return the Hello World message. To
do that we need to implement a new Dojo JavaScript class called HelloWorldTextWidget. The
widget is pure client side resource stuff so we need to add the files involved into the exploded
Share web app (this is just because we are not using a build project). Create a file called
HelloWorldTextWidget.js and put it in the alfresco/tomcat/webapps/share/js/example/
widgets directory:

define(["dojo/_base/declare",
"dijit/_WidgetBase",
"alfresco/core/Core",
"dijit/_TemplatedMixin",
"dojo/text!./HelloWorldTextWidget.html"
],
function(declare, _Widget, Core, _Templated, template) {
return declare([_Widget, Core, _Templated], {
templateString: template,
i18nRequirements: [ {i18nFile: "./
HelloWorldTextWidget.properties"} ],
cssRequirements: [{cssFile:"./HelloWorldTextWidget.css"}],

buildRendering: function
example_widgets_HelloWorldTextWidget__buildRendering() {
this.helloWorldMsg = this.message('hello.world');
this.inherited(arguments);

}
});
});

This widget is based on an HTML template defined in a file called HelloWorldTextWidget.html,


create this file in the same place as the Widget class:

<div class="helloWorldMsgStyle">${helloWorldMsg}</div>

The widget also uses a property called hello.world that needs to be available in a resource file
called HelloWorldTextWidget.properties, create it in the same place as the Widget class:

hello.world=This is just a test page. Hello World! (Aikau)

Finally the widget template uses a CSS style called helloWorldMsgStyle that needs to be
available in a resource file called HelloWorldTextWidget.css, create it in the same place as the
Widget class:

.helloWorldMsgStyle {
border: 1px #000000 solid;
padding: 1em;
width: 100px;
background-color:lightgrey;
}

Now restart Alfresco Content Services and then access the page with the http://
localhost:8080/share/page/hdp/ws/helloworld URL. You should see the following page in
Share:

Developer Guide 28
Developer Guide

The page we choose as a basis (that is, the Hybrid Dynamic Surf Page - hdp) provides both the
header and the footer for the Share web application. If you want to see the page stand-alone you
can use the dp page as a basis.
So when we are working with Aikau pages we do not have to bother about the Site Data model
and all the different kinds of XML files. We just create a web script where the controller will
contain the complete layout of the page. And then the page content will go into an auto-generated
region on the Surf page we select.
Back to Introduction to Surf Pages
Back to Share Architecture and Extension Points

Surf Framework Guide


Surf lets you build user interfaces for web applications using server-side scripts and templates
without Java coding, recompilation, or server restarts. Surf follows a content-driven approach,
where scripts and templates are simple files on disk so that you can make changes to a live site
in a text editor.
Surf is a Spring framework extension for building new Spring framework applications or plugging
into existing Spring web MVC (Model, View, Controller) applications. Spring Web MVC provides
separation between the application Model, View, and Controller (known as MVC). You can use
Surf with other popular Spring Web MVC technologies including Tiles, Grails, and Web Flow.
Surf's object model lets you define pages, templates, components, and themes using XML.
The Spring application picks up new files and processes them through scripts and templates to
produce the view, and writes scripts using server-side JavaScript and Groovy. Templates are
written using FreeMarker. You can build both page-centric and content-centric websites using
Surf, and it provides out-of-the-box support for rendering content delivered through content
delivery services, such as CMIS, Atom, and RSS.
The Groovy invokedynamic indy library is included in Alfresco Content Services.
Depending on the JVM version, you can target close to Java performance for dynamic
Groovy with invokedynamic support activated.

Features
• Scripts and templates: Everything in Surf consists of scripts, templates, or configuration.
This means no server restarts or compilation.
• Reusability: Surf’s presentation objects, templates, and scripts emphasize reusability.
Scoped regions and component bindings allow you to describe presentation with less
code.
• Spring Web MVC: Surf plugs in as a view resolver for Spring Web MVC, enabling you to
use Surf for all or part of a site's view resolution. Surf renders views on top of annotated
controllers and is plug-compatible with Spring Web Flow, Spring Security, Spring Roo, and
Spring tag libraries.

Developer Guide 29
Developer Guide

• RESTful scripts and templates: All page elements and remote interfaces are delivered
through a RESTful API. The full feature set of web scripts is available to Surf applications.
Write new remote interfaces or new portlets with a script, a template, and a configuration
file.
• Content management: A set of client libraries and out-of-the-box components streamline
interoperability with CMIS content management systems, letting you easily access and
present Enterprise content using Surf components and templates.
• Two-tier architecture: Surf works in a decoupled architecture where the presentation tier
is separate from the content services tier.
• Production, development, and staging/preview: Configure Surf to work in a number of
deployment scenarios including development, preview, or production environments.
• Development tools: Tools that plug into the SpringSource suite of development tools
include Eclipse add-ons for SpringSource Tool Suite, as well as Spring Roo plug-ins to
enable scaffolding and script-driven site generation.

Surf content types


In Spring Surf, content is devided into Semantic content and Presentation content, and the web
application looks at both to render the final look of the Web page.

Semantic content
Semantic content represents the domain specific content that should be displayed on the web
page. This can be for example project information,document information, customer information,
discussion thread, wiki page data, workflow task information. This content is typically authored,
approved, and published.
Semantic content describes what should be rendered. It contains the approved message but it
does not contain any formatting information. It is represented in a structured data format, such as
JSON or XML. The following code is an example of JSON text for a biography:

{
"author": "Pablo Neruda",
"country": "Chile", "image": "pablo_neruda.jpg",
"description": "Pablo Neruda is adored in South America for his romantic
prose.",
"popular_works": ["Cien sonetos de amor", "Confieso que he vivido"]
}

This code does not contain any formatting like HTML tags or other markup, just simple data. This
type of content is usually fetched by a Spring Surf Web Script dynamically.

Presentation content
Presentation content are files that describe presentation configuration for a website, it is
represented in XML and FreeMarker template files. Presentation content comprises configuration
that tells the Surf rendering engine how the Web page or page component should look and feel.
Presentation content answers questions like:
• What is the layout of the Web page
• Which theme to use to render the Web page
• How many articles to display on a Web page
• Which advertisement to display in a particular section of the site for the current user

A website presentation framework, such as Surf, looks to this content to figure out how to display
the page. For example, the following XML code configures a Surf Web Script for rendering a

Developer Guide 30
Developer Guide

biography to end users and tells the web script to render a link to the full article as well as to show
the image of the author.

<component>
<url>/content/display/biography</url>
<properties>
<link-title-to-full-article>true</link-title-to-full-article>
<show-image>true</show-image>
</properties>
</component>

These properties are custom properties that are just passed on to the Web Script implementation.
This component definition tells Surf to call this Web Script (i.e. /content/display/biography)
to render the biography.

Semantic and Presentation content work together to render the Web page.
The end-to-end rendering flow is illustrated in the following figure.

This figure depicts a simple request for a biography of a poet. Here is what happens:
1. Surf receives the browser request.
2. Surf asks the content delivery services for the Presentation Content that describes what is
being requested.
3. The Presentation Content is handed back as XML and FTL.
4. Surf determines the Web Script to execute using the configuration specified by the
component XML.
5. The Web Script Controller (JavaScript or Java) calls a service somewhere to retrieve
biography data.
6. The biography is returned as JSON.
7. The Web Script Template (FreeMarker) renders HTML markup to the end user. This HTML
contains presentation output (formatting) as well as the semantic data (the biography
itself).

The website user then sees the poet's content. Rending a complete Web page with more stuff
then just the biography, such as title, footer, header etc, will loop through step 4 - 7 multiple
times.

Content delivery services

Developer Guide 31
Developer Guide

Surf connects to content delivery services to provide content retrieval and query for presentation
and semantic content.
This means that Surf applications can consist of either single-tier or dual-tier applications. The
following figure shows three valid configurations for Surf. The standalone configuration on the left
shows, all the presentation and semantic content stored as part of the Surf web application. It is
self-contained and has everything that it needs so that requests can be services entirely from the
web application in the presentation tier. This is a perfectly acceptable configuration. Surf imposes
no requirements, it does not require a database, any local persistence, or even a user session.

A more interesting scenario is the full content services configuration on the right, where the Surf
application lives in the presentation tier but relies upon content delivery services in the content
services tier to hand it data so that it can respond to incoming requests. This data consists of
things like the biography or web script configuration information. In this case, Surf provides
developers with connector and credential management so that interactions with the content
delivery services can be performed on behalf of the end user. Surf provisions connectors to
web script developers so they can retrieve feeds of data. This data is often represented in either
JSON or XML, but it could also be in any number of other formats. Developers work with these
feeds and render them back through view templates. Surf focuses on view caching and render
performance to minimize the number of remote calls needed on each request.

Surf also has native support for the CMIS standard (Content Management Interoperability
Services), an industry-adopted API for talking to ECM systems. Alfresco Content Services
provides the leading open-source CMIS implementation, as well as an entire suite of tools around
CMIS authoring and delivery. Surf is an ideal presentation technology for CMIS content delivery.
Surf developers have the option to independently scale out the presentation tier from the content
services tier. The presentation tier is primarily developed with the intention of scaling to user load,
while providing quick end-user responses, whereas the content services tier scales to content
retrieval and query. A single page hit to the Surf application could result in several content
services hits, a single hit, or no hits at all depending on the application design.

Model-View-Controller
MVC applications use a dispatcher to handle requests for an application. It looks at the URL to
determine which controller to invoke to set up a model, and then which view to invoke to render
the model.
The dispatcher uses mappings (usually URL mappings) to determine which controllers to invoke
for the incoming URL. It also uses mappings to figure out which view to invoke to render the
response back. A controller contains business logic that should run before the response is
generated. It can do things like query a database or call out to a service. Its job is to place this
data into the model. A view contains rendering logic responsible for building the response that the
end user receives. It looks to the data in the model to inform its rendering process.
The following figure illustrates the process:
1. The dispatcher handles an incoming request. Imagine that the incoming URI is /hotels.

Developer Guide 32
Developer Guide

2. The dispatcher tries to find a matching controller for this URI.


3. A controller is invoked if found. The controller calls out to some services to retrieve a list of
hotels, creates a model, and then places this list of hotels into the model.
4. The dispatcher tries to find a matching view for this URI.
5. A view is invoked if found. The view receives the model and uses it to render HTML
markup that displays the list of hotels.
6. The response is sent back to the end user.

The main benefit of the MVC pattern is that it clearly separates the business and rendering
logic. This modularizes your application architecture, allowing you to plug in new views and new
controllers. It provides reuse, as many views can share a single controller, or many controllers
can share a single view.
Spring Web MVC
Spring Web MVC is the Model-View-Controller implementation for Spring framework web
applications.
Based on Spring configuration, Java beans implement controllers, views, the model, and the
mappings between URIs and handlers. Spring Web MVC is very extensible, letting you write your
own Java code, plug in new beans, or rewire things through configuration.
The dispatcher for a Spring Web MVC application is the dispatcher servlet, which handles the
request, executes the MVC pattern, and tries to identify a controller to handle the incoming
request. A controller is a Plain Old Java Object (POJO) registered with the Spring framework. In
the Spring framework, the model is a simple map of named properties. The controller computes
these, and when finished, hands the model back to the dispatcher servlet. The dispatcher servlet
then tries to determine a view to use to render the model to the end user. It consults a registry of
view resolvers and asks each of them if they can handle the incoming request URI. If it finds a
matching view resolver, it is used to produce a view object, rendering the model to the end user.
Typically, Spring Web MVC application developers focus most of their effort writing controllers
and views in Java, which are then wired together with Spring configuration XML.
Surf View Composition framework
Surf provides a view composition framework.
Surf provides a View Composition framework that lets you define user interface items such as:
• Pages
• Templates
• Regions
• Components

Developer Guide 33
Developer Guide

Pages
You can define views of pages including specific pages, page formats, and page types.
Surf page XML defines Surf pages. A page binds to a URI. Surf allows you to render pages by
passing the URI in the request.
• Request a page using a URL as follows:
http://localhost:8080/surf/<page-id>
http://localhost:8080/surf?p=<page-id>
If Surf finds this page, it looks at the page XML configuration to determine which template
to use to render the output. Each page can have multiple templates keyed by format. A
page might have a default template that it uses for HTML output to a browser, but it can
have another template configured that it uses for output to a wireless device.
• Request a particular format for a page using URLs as follows:
http://localhost:8080/surf/<page-id>?f=<format-id>
http://localhost:8080/surf?p=<page-id>&f=<format-id>
This allows you to have different markup for different intended formats, such as for small
display devices or integration purposes. Surf pages are also locale aware. This lets you
finely adjust your site’s pages for different languages and localization needs. When you
make a request for a page, Surf will do its best to find a match for your browser’s locale. If
a locale match cannot be made, Surf will fall back to a specified default locale.
• Request a page type using one of the following formats:
http://localhost:8080/surf/pt/<page-type-id>
http://localhost:8080/surf?pt=<page-type-id>
Surf lets you group pages into page types. By requesting a page type, Surf will determine
which page to use to satisfy your request. Surf defines a login page type. Your site might
have two themes, such as a normal theme and a holiday theme. You can also have two
distinct login pages, such as a normal login page and a holiday login page.
When the holiday theme is active, you would like Surf to resort to using the holiday login
page. All you have to do is switch the theme for the site. None of the links or URLs change
at all. The URLs in this example, will always take you to the theme’s designated login
page.
• Request a login page type using one of the following formats:
http://localhost:8080/surf/pt/login
http://localhost:8080/surf?pt=login
As before, you can request a particular format of the login page type by using a format
parameter. Here are two URLs that request the wireless format of the login page type.
http://localhost:8080/surf/pt/login?f=wireless
http://localhost:8080/surf?pt=login&f=wireless

Templates and regions


A template is a reusable layout that you can build once and then apply it to many pages. Each
page can then benefit from a common look-and-feel that was pre-prescribed. By changing
the template, a web designer can affect many pages all at the same time. This is especially
meaningful for large websites where you might need to manage hundreds, if not thousands, of
pages.
When Surf looks at a URI and determines the request, it starts the process of handling the view.
The request might be for a specific page, a content item of type “article”, or a specific region of
the current page (for example, in an AJAX request). Regardless of the request, the objective will

Developer Guide 34
Developer Guide

eventually produce markup and send it out to the response. The key to making this happen is the
template, which provides the basic layout of the response to the browser.
The following figure shows three pages for a sample website. Two of the pages are similar with
the same page layout. They have four regions on the page, whereas the first page has only three
regions. As such, you can describe these three pages with two templates. The templates are
shown underneath the pages.

The first template defines three regions with placeholder names (HEADER, BODY, FOOTER).
The second template defines four regions (HEADER, MENU, CONTENT, FOOTER). The
HEADER and FOOTER regions are common across all three pages. You can define the two
templates in Surf and define regions along with region scope to allow reuse across templates, as
illustrated in the following figure.

In this figure, the region scope defines the entire website with only two templates and five scoped
regions. There are three scopes: global, template, and page. Regions in the global scope need
to be configured only once. Then, their configuration is reused by any templates or pages that
include them. In this case, the HEADER and FOOTER regions are defined once in the global
scope. Their content appears the same on all of the pages of the site.
Regions in the template scope need to be configured once per template. Any pages that use the
template then reuse their configuration. In this case, the MENU region is defined in the template
scope for one of the templates, but not the other. Thus, the two pages on the right side that
use this template will have the MENU region in common. Regions in the page scope must be
configured once per page, and their configurations are not reused. In this case, the BODY and
CONTENT regions are in the page scope. This allows the two right-hand pages to be slightly
different, but only in the CONTENT region.
The region tag defines regions on a template with the scope, such as page, template, or global.
The following examples show how this is done in FreeMarker:

Globally scoped header region:

Developer Guide 35
Developer Guide

<@region id="header" scope="global" />

Template scoped navigation region:


<@region id="navigation" scope="template" />

Page scoped content region:


<@region id="content" scope="page" />

A template defines the basic structure of the rendered view, and then defines regions into which
to include additional presentation. The following figure shows an example of the template that
defines four regions: HEADER, MENU, CONTENT, and FOOTER. Sample code after the figure
suggests how you could weave this into a FreeMarker template. It is up to Surf to resolve what to
place in each of these regions when the template is rendered.

<html>
<head>
${head}
</head>
<body>
<div class="header">
<@region id="header" scope="global" />
</div>
<div class="menu">
<@region id="menu" scope="template" />
</div>
<div class="content">
<@region id="content" scope="page" />
</div>
<div class="footer">
<@region id="footer" scope="global" />
</div>
</body>
</html>

When the template is processed, each of its region tags executes and attempts to look up
something that should be included in that location in the template. The region tag is replaced by
the output of something that is bound into that place in the template.

Components

Surf lets you bind components to regions. A component usually associates a region with a web
script.

Templates and scoped regions make it possible to reuse web scripts. You can have as many web
scripts as you like, each encapsulating a unique bit of application functionality.

For example:

Developer Guide 36
Developer Guide

A template brings several web scripts together into an overall markup structure. Here, you are
rendering a page; however, the same concepts apply for any kind of view rendered from Surf
using a template. Surf lets you define regions in various scopes and then resolves these upon
request. This makes your site definition efficient and easier to manage by promoting reuse.
Alfresco Share is an example of a Surf application whose pages are constructed through reuse
of templates. All three scopes are used. The following figure provides an example of how this fits
together.

You can make changes to Alfresco Share pages by tweaking FreeMarker templates and web
scripts. In Surf, web scripts not only provision remote interfaces to your applications, but also
provide your application's presentation logic. These are presentation tier web scripts. Surf
orchestrates them so they can all live together on a single view and interoperate against a
common request context.
There are many more capabilities, such as pre-processing controllers to generate markup that
should be injected into different parts of a page (for example, the <head> of an HTML page). Surf
also provides additional web script and template API root-scoped objects and methods.
Presentation content
Presentation content consists of templates, scripts, and XML files that Surf can pick up without a
server restart.
Surf consults presentation content when it renders the user interface. Surf's presentation content
consists of three kinds of files:
• Surf objects
• Templates

Developer Guide 37
Developer Guide

• Web scripts

Surf objects
Surf objects define website parts and describe how they fit together to build the complete web
application structure.
Objects describe things like pages, page hierarchy, chrome, or components that are reused
across many pages. XML files define objects that are generally short. A single Surf application
will have many XML files to define its objects. When Surf starts up, it looks for all these small
XML fragments and gathers them to form a complete registry of all of the objects.
Example of the XML for a Page object:

<?xml version='1.0' encoding='UTF-8'?>


<page>
<id>mypage</id>
<title>My First Page</title>
<description>This is an example of the XML for a Page</description>
</page>

A Spring project generally maintains these XML files as part of its project resources. They can
reside under the WEB-INF directory or inside the classpath. Users can also manage these files
inside the Alfresco Content Services server, where XML files can be individually managed,
authorized, and approved as part of a lifecycle process. Once approved, these files are available
to the Surf application.
Here are a few examples of the various presentation objects that Surf provides:
• Chrome-application borders for regions and components
• Components-binds web scripts into templates and pages
• Content Instance-points to pages or templates to use when rendering content types
(cm:content or my:article)
• Page - a navigable location within a site
• Page Type - indirection to non-navigable locations, such as a login page
• Template Instance - configuration for dynamic templates
• Theme - settings that define the site experience

Templates and Surf


Templates are transformers that generate markup for the browser to render.
This markup is generally HTML for a website. Templates are applied to the current request
context or model. Templates are often files that contain a composite of the output markup and
processing tags. The tags execute and generate markup that is injected into the template at the
location of the tag. This pattern is common for template types such as FreeMarker, PHP, and
XSL.
Surf supports several useful tags out of the box. One commonly used tag is the region tag, which
tells the template to look up a component and render its output at the location of the tag.
Here is an example of what a FreeMarker template responsible for rendering a page looks like in
Surf:

Developer Guide 38
Developer Guide

<html>
<head>
${head}
</head>
<body>
<div class="header">
<@region id="header" />
</div>
<div class="body">
<@region id="body" />
</div>
</body>
</html>

A Spring project generally maintains these template files as part of its project resources. They
can reside under the WEB-INF directory or inside the classpath. Users can also manage these
files inside a content application server.
Web scripts and Surf
Surf lets you reuse web scripts and bind them together into unlimited numbers of pages. The
scripts are lightweight, making them easy to assemble and deploy.
Declarative web scripts implement a Model-View-Controller pattern. They have a single descriptor
XML file that tells the web script dispatcher how to behave. Declarative web scripts have their
own template views and optional scriptable controllers. You write new views by writing new
template files. You write new controllers by writing new script files. Your scriptable controllers
populate the model variable (a map). Your view uses the model during render. Surf allows you
to merge your web scripts into the rendering of the overall page. that is, Surf lets your web script
MVC participate in the overall Spring MVC.
Surf provides each web script with the appropriate context and runtime environment to render in
the context of the overall request. The output of each web script merges with the output of the
template to form the final markup. This markup is returned from the Spring MVC view.
For example:

Rather than produce 100% of the output itself, the rendering template occasionally delegates
work to the web script runtime when the region tags execute. The web script runtime executes
miniature, scriptable MVC processes whose output merges into the overall rendition. The web
script runtime can use and take advantage of the full request, user, and page context. You can
build web scripts to define component implementations that can be accessed either standalone
or stitched into an overall page presentation. A component can be like a widget or a gadget;
something that you can plug into a website on one or more pages as a reusable bit of application
functionality that participates in the overall page experience.
Web scripts can also be invoked standalone in that they can run outside the context of a page.
You can surface components in menus or refresh portions of a web page using AJAX callbacks.

Developer Guide 39
Developer Guide

Surf also provides portlet capabilities that wrap web scripts and components as JSR-268 portlets
and dropped into portal servers.
A Spring project generally maintains web script files as part of its project resources. They could
reside under the WEB-INF directory or inside the classpath. Users can also manage these files
inside a content application server.
Connectors and credentials
Web script developers often work with remote sources of data. Surf makes it easy to reach out to
these information sources and pull together feeds of data.
These data sources are typically RESTful providers, CMIS repositories, or proprietary in nature.
Furthermore, each data source might require a unique set of credentials to work with the data
source.
Surf lets you define connectors responsible for communicating with endpoints where a data
source lives, such as a server residing at an HTTP address. Connectors connect to an endpoint
and communicate with it.
Connectors are wired together with authenticators so that they can effectively handshake and
establish credentials with endpoints. This pattern abstracts away any of the manual management
of connection state that you would otherwise need to perform. Using authenticators, connectors
manage user identity and session state to the endpoint. This is automatically managed for the
duration of the user session in the Surf application itself.

Connectors and endpoints


Connectors and endpoints are both defined through simple configuration as part of Surf's remote
configuration block.
Declaring an endpoint is fairly simple. It will look something like this:

<config evaluator="string-compare" condition="Remote">


<remote>
<endpoint>
<id>springsurf</id>
<name>Alfresco Surf</name>
<connector-id>http</connector-id>
<endpoint-url>http://www.springsurf.org</endpoint-url>
</endpoint>
</remote>
</config>

This defines an endpoint named springsurf. When talking to this endpoint, a connector of type
http should be used. The data source lives at www.springsurf.org:8080. Since nothing else is
provided, this is assumed to be an unauthenticated endpoint.
Surf provides a number of out-of-the-box connectors. The http connector lets you connect
to HTTP or HTTPS endpoints. To assert an identity to the endpoint, you can adjust the
configuration:

<config evaluator="string-compare" condition="Remote">


<remote>
<endpoint>
<id>springsurf</id>
<name>Alfresco Surf</name>
<connector-id>http</connector-id>
<endpoint-url>http://www.springsurf.org</endpoint-url>
<identity>declared</identity>

Developer Guide 40
Developer Guide

<username>USERNAME</username>
<password>PASSWORD</password>
</endpoint>
</remote>
</config>

The credentials for an http connector are passed through using basic authentication. The
values USERNAME and PASSWORD are just placeholders for your own values. With an
endpoint defined, you can code against the endpoint and use it without worrying about managing
connection state and asserting credentials. You could use the following Web script controller
code to retrieve something from the springsurf endpoint:

// get a connector to the springsurf endpoint


var connector = remote.connect("springsurf");
// place text file into the model
var txt = connector.get("/sample/helloworld.txt");
model.txt = txt;

The remote root-scope variable provides various methods and accessors for working with
connectors. When it is used, the connection mechanics are abstracted away and your web script
code becomes highly portable from one environment to another, as well as reusable across many
users.
Credentials
Surf provides credential management on behalf of users who access content using connectors. If
a connector needs to know which credentials to attach to a given request during an authentication
handshake, it can call upon the credential vault.
Surf's default credential vault is runtime-only; it is populated and used at runtime. If you restart
the server, the credentials are lost and the user must provide their credentials again the next time
the connector is used. Surf lets you override the credential vault implementation. It provides a
number of additional credential vaults out of the box you can use or base your implementations
on. These include a filesystem– persistent credential vault and a credential vault (where your
credentials are stored in an Alfresco Content Services-managed file).
To use the credential vault, you inform the endpoint that its identity is driven from the current
user. You can make this change to your endpoint definition:

<config evaluator="string-compare" condition="Remote">


<remote>
<endpoint>
<id>springsurf</id>
<name>Alfresco Surf</name>
<connector-id>http</connector-id>
<endpoint-url>http://www.springsurf.org</endpoint-url>
<identity>user</identity>
</endpoint>
</remote>
</config>

Connectors to this endpoint will look for the user’s credentials in the credential vault. If credentials
are not found and the endpoint requires authentication, the connection can fail. However, if
credentials are available in the vault, they will be applied and the connector will access the
endpoint on behalf of the developer without the need for manual login.

Developer Guide 41
Developer Guide

Authenticators
Authenticating connectors are connectors that have authenticators plugged into them. An
authenticator is a class that knows how to perform an authentication handshake with a specific
kind of service or application.
For example, MediaWiki provides a REST-based means for authenticating. You pass in your user
credentials and it hands back an HTTP cookie. This cookie must be applied to every subsequent
request, as MediaWiki looks to it to inform the application of who is making the request.
Alfresco Content Services has a similar REST-based means for authenticating. It is slightly
different in that the RESTful parameters are not the same as those of MediaWiki. Also, Alfresco
Content Services hands back a ticket in an XML return payload. This ticket must be applied to the
HTTP headers of every subsequent call so that Alfresco Content Services knows who is making
the request. Every application has a slightly different way of handling its authentication. For this
reason, Surf makes it easy to write your own authenticators and plug them into your connectors
entirely through configuration.
You define authenticators through configuration as well:

<authenticator>
<id>alfresco-ticket</id>
<name>Alfresco Authenticator</name>
<description>Alfresco Authenticator</description>
<class>org.alfresco.connector.AlfrescoAuthenticator</class>
</authenticator>

You can then bind them to connectors using configuration, or you can write your own connectors:

<connector>
<id>alfresco</id>
<name>Alfresco Connector</name>
<description>Connects to Alfresco using ticket-based authentication</
description>
<class>org.alfresco.connector.AlfrescoConnector</class>
<authenticator-id>alfresco-ticket</authenticator-id>
</connector>

The alfresco-ticket authenticator and the alfresco connector are both available to Surf
developers out of the box to connect to an Alfresco Content Services instance. All you need to do
is define an endpoint that points to an Alfresco Content Services instance and uses the alfresco
connector. Alfresco Content Services connectors use an authenticator to perform a handshake
ahead of any actual interaction. The handshake establishes who the user is and then sets up the
connector session so that subsequent requests contain the appropriate connection information
(cookies, request headers, and so forth). The endpoint definition looks like this:

<endpoint>
<id>alfresco</id>
<name>Alfresco REST API</name>
<description>Alfresco REST API</description>
<connector-id>alfresco</connector-id>
<endpoint-url>http://localhost:8080/alfresco/s</endpoint-url>
<identity>user</identity>
</endpoint>

Developer Guide 42
Developer Guide

This endpoint is named alfresco. It uses an alfresco connector and will draw credentials
from the user’s credential vault. This is all defined in configuration. You could use the alfresco
endpoint to talk to an Alfresco Content Services instance and access its remote API. For
example, you can interact with the CMIS API on the repository. Here is an example of retrieving
XML from the CMIS API:

// get a connector to the alfresco endpoint


var connector = remote.connect("alfresco");
// place CMIS text onto the model
model.cmis = connector.get("/api/path/workspace/SpacesStore");

By simply coding to the remote object, you do not need to worry about how to connect to the
endpoint or pass along user state.
Remote API
The remote root-scoped object lets you connect to remote services and retrieve data feeds.
The basic pattern is to use the remote object to get a connector to a specific endpoint, which is
identified by endpoint ID. For example:
var connector = remote.connect(ENDPOINT_ID);
By filling in ENDPOINT_ID with the correct value, you have a connector to the remote service. The
connector variable is an object with additional methods describing all the ways you can work with
the endpoint.
The following methods are the basic HTTP method types that support the essential CRUD
(create, read, update, delete) operations of most RESTful services. You can use these to work
with services right within your web scripts.:
• post(uri, body)—POSTs content to the given URI
• post(uri, body, contentType)—POSTs content of the specified type to the given URI
• get(uri)—GETs content from the given URI
• put(uri, body)—PUTs content to the given URI
• put(uri, body, contentType)— PUTs content of the specified type to the given URI
• delete(uri)—Invokes a URI as a DELETE request
Surf object XML quick reference (siteData)
Surf objects are defined in XML. This document provides a quick reference guide to the most
commonly used Surf objects, and how they are defined in XML.

File locations
In the following sections you will see that two locations are specified:
• classpath:/alfresco/site-data
• classpath:/alfresco/web-extension/site-data
It's important to note that the alfresco/web-extension/site-data directory will be processed
after the alfresco/site-data directory. Usually core Alfresco Content Services objects would
be located in alfresco/site-data directory, and third-party overrides/extensions would be
located in alfresco/web-extension/site-data.

Component
Component instances describe bindings between a region and a rendering engine that is
responsible for generating the component's markup. Typically the rendering engine is the Surf
web script engine.

Developer Guide 43
Developer Guide

Locations
• classpath:/alfresco/site-data/components
• classpath:/alfresco/web-extension/site-data/components

Definition

<component>
<!-- Required -->
<scope>page | template | global</scope>
<region-id>REGION_ID</region-id>
<source-id>SOURCE_ID</source-id>

<!-- Optional -->


<url>URL</url>
<component-type-id>COMPONENT_TYPE_ID</component-type-id>
<chrome>CHROME_ID</chrome>
</component>

Properties
• <id> - Component IDs follow a convention:
• For page and template scoped region bindings the convention is
<scope>.<region-id>.<source-id>
• For bindings to regions in the global scope the convention is global.<region-id>
• <scope> - The scope of the binding (page, template, global)
• <region-id> - The name of the region that is being bound.
• <source-id> - The ID of the page or template instance to which the component is bound.
For the global scope this should be set to global.

Optional properties
• <url> - The web script URL (if a web script is being rendered)
• <component-type-id> - The ID of the component type for this component.
• <chrome> - The ID of the Chrome used to frame this component's presentation.

Example - Page scope binding


This component binds the web script with the URL /sample/content to the paged-scoped region
named content on the page home. It therefore has the ID page.content.home.

classpath:/alfresco/web-extension/site-data/compnents/
page.content.home.xml

<?xml version="1.0" encoding="utf-8"?>


<component>
<id>page.content.home</id>
<scope>page</scope>
<region-id>content</region-id>
<source-id>home</source-id>
<url>/sample/content</url>
</component>

Developer Guide 44
Developer Guide

Example - Template scope binding


This example binds the web script with the URL /sample/header to the template-scoped region
named header on the home template. It therefore has the ID template.header.home.

classpath:/alfresco/web-extension/site-data/compnents/
template.header.home.xml

<?xml version="1.0" encoding="utf-8"?>


<component>
<id>template.header.home</id>
<scope>template</scope>
<region-id>header</region-id>
<source-id>home</source-id>
<url>/sample/header</url>
</component>

Example - Global scope binding


This example binds the web script with the URL /sample/footer to the global-scoped region
named footer. It therefore has the ID global.footer.

classpath:/alfresco/web-extension/site-data/compnents/global.footer.xml

<?xml version="1.0" encoding="utf-8"?>


<component>
<id>global.footer</id>
<scope>global</scope>
<region-id>footer</region-id>
<source-id>global</source-id>
<url>/sample/footer</url>
</component>

Example - Custom page scope binding


This example binds the web script with the URL /sample/content to the page-scoped region
named content on the page home. It informs Surf to wrap the Component with a custom
component Chrome when it renders. It also provides a few custom properties that the web script
can use while it executes

classpath:/alfresco/web-extension/site-data/compnents/
page.content.home.xml

<?xml version="1.0" encoding="utf-8"?>


<component>
<id>page.content.home</id>
<scope>page</scope>
<region-id>content</region-id>
<source-id>home</source-id>
<url>/sample/content</url>
<chrome>sample-chrome</chrome>
<properties>
<view>FULL</view>
<style>formal</style>
</properties>

Developer Guide 45
Developer Guide

</component>

Configuration
Configuration files let you store arbitrary XML descriptions for use in your custom Surf objects. In
most cases, the only time you will need to construct one of these objects is when describing site
configuration. An example of a site configuration follows.

Locations
• classpath:/alfresco/site-data/configurations
• classpath:/alfresco/web-extension/site-data/configurations

Definition

<configuration>
<source-id>SOURCE_ID</source-id>
</configuration>

Properties
• <source-id> - Tags the configuration as pertaining to an arbitrary ID. Surf will
automatically look for configuration where source-id is site.

Example - Site configuration


The following file defines the Surf site configuration. It describes a Configuration object that is
bound to the site source ID.

classpath:/alfresco/web-extension/site-data/configurations/
default.site.configuration.xml

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<title>My Web Site</title>
<source-id>site</source-id>
<properties>
<root-page>welcome</root-page>
</properties>
</component>

Page
A page describes a URL-addressable destination that has been resolved and for which a view
must be produced. A page aligns with the concept of a web page from the end user's point of
view. Pages are often arranged into page hierarchies that constitute a web site's navigation
structure.
Pages can specify whether they require user authentication before rendering. Unauthenticated
users will not be able to render the page.
Pages also have optional types that allow them to be dispatched by page type rather than Page
ID. Pages have one or more template instances associated with them. This allows them to render
distinctly for different intended output formats (for example, HTML, PDF, or wireless).

Developer Guide 46
Developer Guide

Locations
• classpath:/alfresco/site-data/pages
• classpath:/alfresco/web-extension/site-data/pages

Defnition

<page>
<!-- Optional authentication setting -->
<authentication>none | user</authentication>

<!-- Optional page type ID (otherwise assumes generic) -->


<page-type-id>PAGE_TYPE_ID</page-type-id>

<!-- Use this to associate a default template -->


<template-instance>TEMPLATE_ID</template-instance>

<!-- Use this to associate a template to this page for a given format -->
<template-instance format="FORMAT_ID">TEMPLATE_ID</template-instance>
</page>

Properties
• <template-instance> - The IDs of one or more template instances that will be used to
render this Page when requested for a give FORMAT_ID. If the format attribute is not
supplied, it is assumed to have the value default.

Optional properties
• <authentication> - the level of authentication required in order for the end user to
access this page. Valid authentication values are none or user (defaults to none).
• <page-type-id> - the ID of the page type of this page.

Example - page with authentication


The following file defines a page called "products". Only authenticated users are allowed to
access the page. When the page is asked to render in the default format, it looks to the template
instance with the ID landing1. When the page is asked to render in the print format, it looks to
the template instance with the ID landing1-print.
Were you to set up the landing1-print template, you would be able to request the print format
for this page using the following URL:
http://localhost:8080/webapp/page/products?f=print

classpath:/alfresco/web-extension/site-data/pages/products.xml

<?xml version="1.0" encoding="utf-8"?>


<page>
<id>products</id>
<title>Products Page Title</title>
<description>Products Page Description</description>
<authentication>user</authentication>
<template-instance>landing1</template-instance>
<template-instance format="print">landing1-print</template-instance>
</page>

Developer Guide 47
Developer Guide

Template instance
Template instances wrap configuration around a template file. The template file receives all the
properties of the template instance and can use these properties to inform its rendering logic.
This empowers a single template file to render differently based on the configuration stored in the
template instance.
For simple cases where the template instance is not required to store additional configuration, it
may remain a very lightweight pointer to the template file. For more advanced cases, the template
instance may store render-time information concerning how to lay out elements on the page.

Locations
• classpath:/alfresco/site-data/template-instances
• classpath:/alfresco/web-extension/site-data/template-instances

Definition

<template-instance>
<template-type>TEMPLATE_TYPE_ID</template-type>
</template-instance>

Properties
• <template-type> - the ID of the template type used to render this template instance. If
a template path is provided here, the template type is assumed to be FreeMarker and the
path is used for rendering.

Example - landing template with configuration

classpath:/alfresco/web-extension/site-data/template-instances/landing1.xml

<?xml version="1.0" encoding="utf-8" ?>


<template-instance>
<id>landing1</id>
<template-type>landing</template-type>
<properties>
<columns>2</columns>
<rows>3</rows>
</properties>
</template-instance>

Template type
Template types contain information that is common across many template instances of the same
type. A template type defines one or more rendering processors. It maybe also define properties
that all template instances of the given type will receive at render time.
When the framework needs to render a template instance, it considers the template type and
merges its properties forward. The uri of the template instance overrides the uri of the template
type.

Developer Guide 48
Developer Guide

Locations
• classpath:/alfresco/site-data/template-types
• classpath:/alfresco/web-extension/site-data/template-types

Definition

<template-type>
<!-- Required "view" processor -->
<processor mode="view">
<id>PROCESSOR_ID</id>

<!-- Optional Uri -->


<uri>PROCESSOR_URI</uri>
</template-type>

Properties
• <processor> - identifies the rendition processor to use. Valid PROCESSOR_ID
values include freemarker, jsp, and a custom ID. With the FreeMarker processor,
PROCESSOR_URI should identify the path to the FTL file relative to the /templates
directory. With the JSP processor, PROCESSOR_URI should identify the path to the JSP
file relative to the web application root.

Example - FreeMarker template processor


The following file defines a template type that is used by template instances to invoke the
FreeMarker processor.

classpath:/alfresco/web-extension/site-data/template-types/freemarker.xml

<?xml version="1.0" encoding="utf-8" ?>


<template-type>
<id>freemarker</id>
<title>FreeMarker Template Type</title>
<processor mode="view">
<id>freemarker</id>
</processor>
</template-type>

Theme
Themes capture default settings for the rendering of elements in the request. A theme is a unique
identifier as well as a collection of properties and page type overrides. When a theme is selected,
its properties and its page type overrides apply to the request.
A theme captures default settings for the rendering framework. Different themes can have
different rendering behaviors.

File locations
In the following sections you will see that two locations are specified:
• classpath:/alfresco/site-data/themes

Developer Guide 49
Developer Guide

• classpath:/alfresco/web-extension/site-data/themes

Definition

<theme>
<!-- Optional page type overrides -->
<page-types>
<page-type>
<id>PAGE_TYPE_ID</id>
<page-id>PAGE_ID</page-id>
</page-type>
</page-types>
</theme>

Properties
• <page-types> - one or more optional overrides that assign page instances to be used
when Surf asks for a Page of a particular type. Using this mechanism, themes can swap
out different default Pages to significantly affect the look and feel.

Example
The following file defines a theme that overrides the login page type to include a different default
page. When this theme is used, Surf will render back the default-login-page Page when the
login page type is requested.

classpath:/alfresco/web-extension/site-data/themes/default.xml

<?xml version="1.0" encoding="utf-8"?>


<theme>
<id>default</id>
<page-types>
<page-type>
<id>login</id>
<page-id>default-login-page</page-id>
</page-type>
</page-types>
</theme>

Advanced Surf Topics


Advanced topics in the Surf development framework.
Specific information:
• How checksums work
• CSS data image support
• Dependency aggregation
Surf Checksums
Checksums can be applied to JavaScript and CSS resources to facilitate efficient upgrading of
Alfresco Content Services.
One of the problems that has affected upgrades of Alfresco Content Services in the past is that
the end-user's browser can end up using cached copies of JavaScript and CSS files that have

Developer Guide 50
Developer Guide

been updated during the upgrade. Surf has a service called the “DependencyHandler” which
solves this specific problem.
The approach taken is to append a unique checksum to the end of each requested JavaScript
and CSS dependency, where the checksum is generated from the contents of the file. If file
content is changed the checksum generated will be different. The checksum associated with
the file is cached for the lifecycle of the web server - this means that it does not need to be
generated for each request. Surf performance has actually been enhanced by this mechanism
because Surf also caches the location from which the dependency was retrieved Surf can retrieve
dependencies from a number of different locations, for example, JAR files, class path, file system,
remote location, and so on.
This feature is enabled by default in the webapps/share/WEB-INF/surf.xml file:

<web-framework>
...
<use-checksum-dependencies>true</use-checksum-dependencies>
...
</web-framework>

Note that this value should not be changed for Alfresco version 4.2 and later, or Alfresco
3.4 and earlier. It can however be changed for Share in Alfresco versions 4.0 and 4.1.
In order to make use of the Dependency Handler you will need to use the <@script>, <@link>
and <@checksumResource> FreeMarker directives in your template instance and web script
files, for example:

<@script src="${url.context}/res/yui/yahoo/yahoo.js"></@script>
<@link rel="stylesheet" type="text/css" href="${url.context}/res/css/
base.css" />
<@checksumResource src="${url.context}/res/css/ipad.css"/>

These examples are taken from the resources.get.html.ftl file where the ${url.context} is
a FreeMarker variable set to the application context, for example share.
<@script>
Generates JavaScript script import declarations.
<@link>
Rolls up multiple CSS requests into a style declaration using a separate @import statement
for each usage of <@link>.
<@checksumResource>
Generates just the URL, that is without being specific to CSS or JavaScript and can
therefore be used with images or even web script requests. One additional feature of the
<@checksumResource> directive is that you can specify the attribute parameter which
makes the checksum appear as the value of a request parameter of the supplied name
(rather than as part of the file name itself).

Debug and production suffices


The Dependency Handler is capable of dealing with production (minified) and debug versions of
files. The Spring application context configuration for the bean allows you to specify the different
file suffices that can be used for both production and debug versions and the Dependency
Handler will work through the different suffices until it finds a matching file. This means that Surf
will always be able to fall back to the debug version of the code if a minified version does not
exist. By default the debug suffices are:
• No suffix

Developer Guide 51
Developer Guide

• _src
• -debug

The production suffices are:


• -min
• -minified
• no suffix

You can change these suffices by overriding the definition for the dependency.handler bean in
the Spring application context in case you want to add, remove or re-order the default entries.

Current limitations
The current limitation of this solution is that it only works with static requests from the page and
not dynamic requests made from a script. However, many JavaScript libraries provide their own
solution to this problem (for example TinyMCE).

Surf CSS data image support


Surf provides support for CSS data URIs. Data URIs are a way of embedding image data into
a CSS page. The advantage of using this approach is that no additional HTTP Requests are
required to fetch and return images. This increases the performance of a web page considerable,
especially for pages with numerous image files.
Much of the performance hit associated with a browser loading a web page comes from the
multiple HTTP Requests (and returns) that are required to load multiple resources required by the
page, particularly image files. One solution to this is to combine the images into a so called CSS
Sprite - this means that a single HTTP request is then required for a page with multiple images.
CSS is then used to locate sub-sections of the CSS Sprite on the web page. While this provides
a performance boost, especially for image rich pages, it still requires an HTTP request to obtain
the CSS Sprite file. Also, the technique requires some preparation on the part of the web page
developer.
An alternative to the CSS Sprite is to use Data URIs. Data URIs effectively allow data to be
embedded within the CSS stylesheet itself. This data can be image data. The advantage of
this approach is that no additional HTTP Requests are required for images beyond the one to
fetch the CSS stylesheet itself. This represents an additional performance boost, and is more
convenient for the web page developer, as it does not require the potentially tricking positioning
code required by the CSS Sprite approach (although in practice this is somewhat alleviated by
web page design tools).
While it might seem that embedding image data into a CSS file has the potential to make CSS
files unweildy, the image data is typically Base64 encoded and gzipped to make it far more
compact.
With regards Share, while the CSS Sprite could be applied to Share pages, it has the potential to
break existing Share customizations, so this appraoch is not used in Share. The approach taken
instead is to use the Data URI approach to embed images in CSS stylesheets.

Configuration in Share
Surf can now automatically produce CSS data images by simply adding the following line to
its configuration file. For Share this configuration file is located in webapps/share/WEB-INF/
surf.xml.

<web-framework>
...

Developer Guide 52
Developer Guide

<generate-css-data-images>true</generate-css-data-images>
...
</web-framework>

Providing that checksum dependencies are also enabled then all images reference by CSS files
will be embedded as data within those CSS files. In addition to eliminating HTTP requests for
images, when an image changes, the CSS checksum will change so the browser will not use
stale cached images. The Dependency Handler service defers some of this work to a dedicated
CssDataImage service which can be overridden in the Spring application context if required.
All images are cached for the life-cycle of the server so that performance is not impeded when
requesting the same image multiple times.
CSS data image files are not supported by Internet Explorer versions 6 and 7. If these
browsers are detected then the CSS data image will not be generated.

Surf dependency aggregation


There are additional FreeMarker template directives related to aggregate dependencies.

Introduction
This information expands on FreeMarker template attributes and directives.

The group attribute


The documentlist.get.html.ftl template uses the group attribute in the <@link>,
<@script>, <@inlineScript> and <@createWidgets> directives. This attribute determines the
order in which dependency requests and JavaScript code are output into the rendered HTML
page.
Surf supports the ability to aggregate multiple files into a single resource to reduce the number
of HTTP requests made by the client, in order to increase page loading performance. The group
attribute is used to determine how dependencies are aggregated into the generated resources.
Managing the groups is important because once generated a resource is cached on the server to
improve response times for subsequent requests. If a single group were to be used then only one
HTTP request would be made per page, but the performance gained through reduced requests
would be lost to server side aggregation for each request.
In order for the same Share code to be able to support different Surf operation modes the group
attribute is also applied when processing individual dependency requests. Groups are output
in the order they are requested and all the dependency requests and code are output for each
group in turn.
By way of example, for the following HTML:

<@script src="/aaa.js" group="1"/>


<@script src="/bbb.js" group="2"/>
<@script src="/ccc.js" group="3"/>
<@script src="/ddd.js" group="2"/>
<@script src="/eee.js" group="1"/>

The output is:

<script src="/aaa.js"></script>
<script src="/eee.js"></script>
<script src="/bbb.js"></script>
<script src="/ddd.js"></script>
<script src="/ccc.js"></script>

Developer Guide 53
Developer Guide

Note that /eee.js is the second requested import despite appearing last in the list and that /
ccc.js is last despite it appearing 3rd. This is because all of group "1" is output before any of
group "2", and all of group "2" is output before group "3".

Mixing <@script> and <@inlineScript>


Given files A.js and B.js and a WebScript template containing the following:

<@script src="${url.context}/res/A.js" group="calc"/>


<@inlineScript group="calc">
// A comment between imports
</@>
<@script src="${url.context}/res/B.js" group="calc">

When the final page is rendered in the source you would see an import like this:

&lt;script type=&quot;text/javascript&quot; src=&quot;/share/res/


A.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;//&lt;![CDATA[
// A comment between imports
//]]&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/share/res/
B.js&quot;&gt;&lt;/script&gt;

Note that the JavaScript from the <@inlineScript> directive is placed between the two imports
because they are in the same group. The same is true for any custom directive that outputs
JavaScript, for example the <@createWidgets> directive.

Configuring Surf to Aggregate Dependencies


To enable the use of aggregate dependencies you will need to make a Surf configuration change.
By default the capability is disabled in Surf and is unlikely to ever enabled by default in future
releases of Alfresco Content Services.
To enable it you set the following line within the Surf configuration file, webapps/share/WEB-INF/
surf.xml:

<web-framework>

<aggregate-dependencies>true</aggregate-dependencies>

</web-framework>

Any third-party modules or add-ons that have been applied might not support this feature.

Aggregated Dependency Output


If you do enable dependency aggregation then you can expect the following behaviour to occur. If
the file A.js contains:

var a = 1;

Developer Guide 54
Developer Guide

and the file B.js contains:

var c = a + b;

and you have a WebScript template containing the following:

<@script src="${url.context}/res/A.js" group="calc"/>


<@inlineScript group="calc">
var b = 1;
</@>
<@script src="${url.context}/res/B.js" group="calc">

When the final page is rendered in the source you would see an import like this:

<script type="text/javascript" src="/share/


res/20146f7250123ea2437a0d16d5c323.js"></script> <!-- Group Name: "calc" -->

The source of that file would contain:

var a = 1;
var b = 1;
var c = a + b;

The resource name is an MD5 checksum generated from the combined source code. The
generated resource is cached on the server so that it doesn't need to be generated each time.
If extra content is added to the group (even dynamically by a module) then the resource will
be regenerated and the checksum will naturally change to ensure that the browser requests a
different file.

Debugging
The <client-debug> setting located in webapps/share/WEB-INF/classes/alfresco/share-
config.xml, will work when enabled, even when using aggregation. An aggregated resource will
still be produced but each aggregated file will be separated by a comment similar to the following:

/*Path=A.js*/

This will allow you to determine the source file in which errors occur when debugging.

The Output Directives


Previous versions of Alfresco Share relied on the use of the ${head} FreeMarker model
property to output all the dependency requests generated on the first pass of all the web script
*.head.ftl files. This property is populated during this first pass and then output in <head>
HTML element defined in the alfresco-template.ftl template. Current code also contains a
reference to that property, as this is used to support legacy *.head.ftl files and dependencies
defined through any <dependencies> elements in extension module configuration. There are
also two new directives: <@outputJavaScript/> and <@outputCSS/>.
As their names suggest these directives are used to output the JavaScript and CSS dependency
requests made by using the <@link>, <@script>, <@inlineScript>, and <@createWidgets>
directives. The output directives act as placeholders in extensibility model and accumulate
requests to output content as the remainder of the Surf page is processed - only when the page
has completely been processed is their final content rendered into the output stream.

Developer Guide 55
Developer Guide

Towards the end of the alfresco-template.ftl file you will also see a commented out
directive <@relocateJavaScript>. The purpose of this directive is to change the location
in the page where JavaScript output is rendered. It is suggested to move JavaScript to the
end of a page to increase page performance. It is only possible to use this directive if there is
no hard-coded <script> elements on the page that depend on imported files or JavaScript
dependencies output by using the ${head} property. When uncommented though you will see
that it produces a very clean source file for your page with all the JavaScript located at the end.
The <@relocateJavaScript> directive is available should you wish to use to in custom Surf
pages.

APIs
To access and extend out-of-the-box services, the content application server exposes two flavors
of API, each designed for a specific type of client.
The two main categories of API that are available to use when interacting with the Alfresco
Repository is the remote and the embedded APIs.
Remote APIs
The main remote Application Programmaing Interface (API) is the , which should be the first place
you go to when you want to interact with the Alfresco Repository remotely. If portability is very
important, than have a look at the , which is a standard implemented by many ECM vendors.
Embedded APIs
The embedded APIs have traditionally been used a lot to build customizations that run inside
the same JVM as the Alfresco Repository. There are both a Public Java API and a Repository
JavaScript API. Before using the embedded APIs a thorough investigation should be done to rule
out the possibility of building the extension with a remote a remote API. It is not recommended
to build embedded extensions unless it is absoutely necessary. They make it difficult during
upgrades and can quite easily have unintended side effects on core repository functionality, such
as file upload.
For an overview of all APIs navigate to this .

Deployment architecture
The way Alfresco Content Services is deployed and run from version 6.0 onwards dramatically
changed from previous versions. Docker and Kubernetes are now used and there are no
installers.
Introduction
The way you deploy and run the Alfresco Content Services solution has changed significantly
since version 6.0. Traditionally you would download an installer that would install Java, Tomcat,
Database, WARs, tools, etc., and things would be configured to work together. Then you would
use a script to kick things off. That’s no longer the case and there are no installers available. We
will be working with Docker containers instead.
It’s now possible to kick off Alfresco Content Services from a number of Docker images. These
images are available in the https://hub.docker.com repository. However, kicking off individual
Docker containers based on these images, and configuring them to work together, might not be
the most productive way to get up and running with Alfresco Content Services. To make things
easier, and achieve a one-click to deploy and run solution, a Docker compose file is available to
quickly deploy and run Alfresco Content Services when you need to test something or work on a
proof-of-concept (PoC).
There are also Helm Charts available to deploy the solution in production as a Kubernetes cluster
in for example AWS.

Developer Guide 56
Developer Guide

Docker Architecture
When Alfresco Content Services is deployed with Docker it looks something like this:

A number of Docker images are deployed to make up the Alfresco Content Services solution:
• alfresco/alfresco-share - the Alfresco Share web interface (i.e. share.war) running on
Apache Tomcat
• alfresco/alfresco-search-services - the Solr 6 based search service running on Jetty
• alfresco/alfresco-content-repository - the Alfresco Repository app (i.e. alfresco.war)
running on Apache Tomcat
• alfresco/alfresco-activemq - the Alfresco ActiveMQ image

There are also other supporting features available, such as Docker images for image and
document transformation:
• alfresco/alfresco-imagemagick
• alfresco/alfresco-libreoffice
• alfresco/alfresco-alfresco-pdf-renderer
• alfresco/alfresco-tika

To build the alfresco/alfresco-content-repository image Alfresco uses the https://


github.com/Alfresco/acs-packaging project. This project also includes a Docker Compose script
that can be used to launch a demo, test, or PoC system. You can customize this script if you

Developer Guide 57
Developer Guide

like in order to run with different versions than those set by default, which are usually the latest
versions.

Kubernetes Architecture
Alfresco Content Services cluster deployments are managed via Kubernetes container
orchestrator. The deployment architecture is defined with the Helm tool.

Deployment options
You can deploy Alfresco Content Services in many different forms and topologies. Because its
infrastructure foundation protects Alfresco Content Services from the environment within which it
executes, you can choose components such as operating system, database, application server,
web browser, and authentication system. It's designed to scale down as well as up.

Embedded Alfresco Content Services


An embedded Alfresco Content Services is contained directly within a host where the host
communicates with Alfresco Content Services through its embedded API, meaning the host and
Alfresco Content Services reside in the same process. Typical hosts include content-rich client
applications that require content-oriented storage, retrieval, and services, but can also include
hosts such as test harnesses and samples. A client can choose to embed the web application
framework or content application server, or both, treating Alfresco Content Services as a third-
party library. In any case, the client can pick and mix the services to embed, allowing very small-
footprint versions. The host is responsible for the start up and shutdown of Alfresco Content
Services.

Content application server


An content application server is a stand-alone server capable of servicing requests over remote
protocols. A single server can support any number of different applications and clients where new
applications can be arbitrarily added. Clients communicate through its Remote API and protocol
bindings, although you can configure a server to omit or prohibit specific access points. This
type of deployment takes advantage of an application server where Alfresco Content Services
is bundled as a web application. Application server features, such as transaction management
and resource pooling, are injected into the infrastructure foundation, allowing Alfresco Content
Services to take advantage of them.
For example, you can embed the content application server inside Apache Tomcat for the
lightest-weight deployment, as well as inside Java Enterprise Edition compliant application
servers from JBoss, Oracle, or IBM to take advantage of advanced capabilities such as
distributed transactions.

Clustered
To support large-scale systems, you can cluster Alfresco Content Services. This lets you set up
multiple servers to work with each other, allowing client requests to be fulfilled across a number of
processors. You can cluster both the web application framework and content application server,
allowing each tier to scale out independently. Each node of a clustered content application server
shares the same content repository store, although the store itself can be replicated across the
nodes, if required. Caches and search indexes are also distributed, meaning that a clustered
content application server looks and acts like a single content application server.
Typically, a load balancer is placed in front of the clustered content application server to distribute
requests across the nodes. This setup also supports Cloud deployments. Alfresco Content
Services provides images and tools for easily deploying a clustered content application server
across multiple Amazon EC2 virtual nodes.

Developer Guide 58
Developer Guide

Backup server
This is a special case of the clustered deployment where, in case of failure, an application can
switch to a backup version of the deployed stack. Depending upon configuration, the backup
version might be available immediately on failure (known as hot backup) or shortly after failure,
following some configuration changes (known as warm backup). One of the nodes in the cluster
is designated the master, which supports the live application, while the other node is designated
the slave, which keeps itself replicated with the master. The slave remains read-only until the
point of switchover.

Multi-tenancy
Multi-tenancy allows a single content application server (clustered or not) to support multiple
tenants, where a tenant such as a customer, company, or organization believes they are the only
user of the server as they connect to a logical partition. Physically, all tenants share the same
infrastructure, such as deployed nodes in a cluster and content, repository storage. However,
data maintained by one tenant cannot be read or manipulated by another tenant. A deployment
of this type eases administration and reduces the cost associated with maintaining many different
applications and user bases, in particular when upgrading core services or performing backups,
as this only needs to be done once for all tenants.
Alfresco Content Services provides administration tools for managing tenants, including the
creation of tenants at runtime. In conjunction with clustering, multi-tenancy provides an ideal
deployment option for the Cloud.

Programming models
A number of programming models are available for building an application using the content
application server.
• The main development framework that should be used when building Alfresco Content
Services clients is the Alfresco Application Development Framework (ADF). It is an
Angular based framework with a number of ACS and APS components that can be used to
build a domain specific UI that meets the specific requirements.
• The simplest model for non-programmers is to use out-of-the-box components of the
Alfresco Share application and the Rules and Actions model, a set of conditions and
actions to take on content based on those conditions. You can define rules and actions
using a wizard and perform actions such as converting content, moving content, or
executing a simple JavaScript snippet.
• Web scripts let you perform more sophisticated processing without complex programming.
The Content Management Interoperability Services (CMIS) implementation was built
using web scripts. By using JavaScript to build these data services, it is easy to create
new services. To build new user interfaces or extensions to Share, you can also use web
scripts by using a web templating language like FreeMarker. Most of Share was built using
web scripts.
• To use Java to build applications or extend Share, you can use the many tools associated
with Java that were used to build the system. Surf, the web runtime framework, lets you
extend Share and build web applications. Because Share was built using Surf, you can
build your own extensions as a combination of Java programming and web scripts, or with
Java alone. You can also use Java to access or even replace whole pieces of Alfresco
Content Services, content application server, or Share by using the Spring platform. You
can use the source code as an example for rewriting pieces and using Spring beans and
configuration to extend or replace functionality in Alfresco Content Services.
• To write applications that use Alfresco Content Services but are portable to other ECM
systems, you can use Content Management Interoperability Services (CMIS), the OASIS
standard for accessing content repositories.

Developer Guide 59
Developer Guide

Repository concepts
It is important as a developer to have a good understanding of the fundamental concepts of
Alfresco Content Services when implementing extensions. Important concepts covered include
repository, nodes, stores, types, aspects and so on.
Overview
The repository is comparable to a database, except that it holds more than data. The actual
binary streams of the content are stored in files managed in the repository, although these files
are for internal use only and don't reflect what you might see through the user interfaces. The
repository also holds the associations among content items, classifications, and the folder/file
structure. The folder/file structure is maintained in the database and is not reflected in the internal
file storage structure.
The repository implements services including:
• Definition of content structure (modeling)
• Creation, modification, and deletion of content, associated metadata, and relationships
• Query of content
• Access control on content (permissions)
• Versioning of content
• Content renditions
• Locking
• Events
• Audits
• Import/Export
• Multilingual
• Rules/Actions
The repository implements and exposes these services through an Alfresco ReST API and CMIS
protocol bindings. The storage engine of the repository stores and retrieves content, metadata,
and relationships.
Key Concepts
All files that are stored in Alfresco Content Services are stored in what is referred to as the
repository. The repository is a logical entity that consists of three important parts:
1. The physical content files that are uploaded, which are stored in the file system.
2. The index files created when indexing the uploaded file so it is searchable, which are
managed by an external Apache Solr server.
3. The metadata/properties for the file, which are stored in a relational database management
system (RDBMS).
When a file is uploaded to the repository it is stored on disk in a special directory structure that is
based on the date and time of the upload. The file name is also changed to the UUID (Universally
Unique Identifier) assigned to the file when it is added to the repository. The file's metadata is
stored in an RDBMS such as PostgreSQL. Indexes are also stored on the disk. When the file is
added to the repository it is stored in a folder, and the folder has domain specific metadata, rules,
and fine grained permissions. The top folder in the repository is called Company Home, although
it will be referred to with the name repository in the Alfresco Share user interface.
When a file is uploaded to the repository it is always classified according to a so called content
model. A content model is one of the first things that will be designed and implemented in a
content management project. It customizes the repository to store information for a specific

Developer Guide 60
Developer Guide

domain, such as healthcare, finance, manufacturing, legal etc. By classifying content (i.e. files
and folders) end users can search and process content more effeciently.
Logical Structure
All the files and folders that are uploaded and created in the repository are referred to as nodes.
Some nodes, such as folders and rules, can contain other nodes (and are therefore known as
container nodes). Nodes can also be associated with other nodes in a peer to peer relationship,
in a similar fashion to how an HTML file can reference an image file. All nodes live in a Store.
Each store has a root node at the top, and nodes can reference specific files, as shown in the
following diagram:

Stores Overview
The Repository contains multiple logical stores. However, a node lives only in one store. Most
of the stores are implemented as data in the connected RDBMS, only the Content Store is
implemented so as to store items on disk:

The main stores


The Working Store (workspace://SpacesStore) contains the metadata for all active/live nodes
in the Repository. This store is implemented using a database (RDBMS).
The Content Store contains the physical files uploaded to the Repository and is located in the
{Alfresco install dir}/alf_data/contentstore directory on the filesystem by default, but
can also be configured to use other storage systems, for example, Amazon S3. It is also possible
to define content store policies for storing files on different storage systems, effectively defining
more than one physical content store.
Whenever a node is deleted, the metadata for the node is moved to the Archive Store
(archive://SpacesStore), which uses the configured database. The physical file for a
deleted node is moved (by default after 14 days) to the {Alfresco install dir}/alf_data/
contentstore.deleted directory, where it stays indefinitely. However, a clean-up job can be
configured to remove the file at a certain point in time (referred to as eager clean-up).

Developer Guide 61
Developer Guide

When the versionable aspect is applied to a node, a version history is created in the Version
Store (workspace://version2Store). Versioned node metadata is stored in the database and
files remain in the {Alfresco install dir}/alf_data/contentstore directory. Versioning is
not applicable to folder nodes.
The System Store is used to save information about installed Alfresco Content Services
extension modules.
Content Store Implementation
When considering file storage, it should be noted that files added to Alfresco Content Services
can be of almost any type, and include images, photos, binary document files (Word, PPT,
Excel), as well as text files (HTML, XML, plain text). Some binary files such as videos and
music files can be relatively large. Content store files are located on the disk, rather than in the
database as BLOBs. There are several reasons for this:
1. It removes incompatibility issues across database vendors.
2. The random file access support cannot be provided by database persistence without first
copying files to the file system.
3. Possibility of real-time streaming (for direct streaming of files to browser).
4. Standard database access would be difficult when using BLOBs as the most efficient
BLOB APIs are vendor-specific.
5. Faster access.

Content Store Selectors


The content store selector provides a mechanism to control the physical location on disk for
a content file associated with a particular content node. This allows storage polices to be
implemented to control which underlying physical storage is used, based on your applications
needs or business policies.
For example, it is possible to use a very fast tier-1 Solid State Drive (SSD) for our most important
content files. Then, based on business policies that have been decided, gradually move the data,
as it becomes less important, to cheaper tier-2 drives such as Fiber Channel (FC) drives or Serial
ATA drives. In this way, it is possible to manage the storage of content more cost effectively:

Store Reference
When working with the APIs a store is accessed via its store reference, for example
workspace://SpacesStore. The store reference consists of two parts: the protocol and the
identifier. The first part (for example workspace) is called the protocol and indicates the content
you are interested in, such as live content (workspace://SpacesStore) or archived content
(archive://SpacesStore). The second part is the identifier (the type of store) for the store, such
as SpacesStore, which contains folder nodes (previously called spaces) and file nodes data, or
for example lightWeightVersionStore that contains version history data.
The reason some things are referred to as spaces (for example SpacesStore) is that in
previous versions of Alfresco Content Services a folder used to be called a space. The
Share user interface has generally been changed to use the name folder instead of the
name space. However, there is functionality, such as Space Templates, that still uses the
term "space". A space can simply be thought of as a folder.

Developer Guide 62
Developer Guide

Node Information
A node usually represents a folder or a file. Each store also contains a special root node at the
top level with the type sys:store_root. The root node can have one or more child nodes, such
as the Company Home folder node. Each node has a primary path to a parent node and the
following metadata:
• Type: a node is of one type, such as Folder, File, Marketing Document, Rule, Calendar
Event, Discussion, Data List and so on.
• Aspects: a node can have many aspects applied, such as Versioned, Emailed,
Transformed, Classified, Geographic and so on.
• Properties: both types and aspects define properties. If it is a file node then one of the
properties points to the physical file in the content store.
• Permissions: access control settings for the node.
• Associations: relationships to other nodes (peer or child).
Node Reference
A node is uniquely identified in the Repository via its node reference, also commonly referred
to as NodeRef. A node reference points to a store and a node in that store. A node reference
has the following format: {store protocol://store identifier}/UUID such as for example
workspace://SpacesStore/986570b5-4a1b-11dd-823c-f5095e006c11. The first part is the
store reference and the second part is a Universally Unique Identifier (UUID) for that store. Node
references are used a lot in the available APIs so it is good to have an idea of how they are
constructed.
Node Properties
The node properties, also referred to as the node's metadata, contains the majority of the
information for a node. The sys:store-protocol, sys:store-identifier, and sys:node-
uuid properties contains all the data needed to construct the NodeRef, uniquely identifying the
node. The special property called cm:content points to where the physical content file is stored
on disk (unless it is a folder or other contentless node type). All properties are either contained in
a type or in an aspect defined in a content model. When a node is created some properties are
automatically set by the system and cannot be easily changed, they are called audit properties
(from the cm:auditable aspect) and are Created Date, Creator, Modified Date, Modifier, and
Accessed. Defining new domain specific node properties, together with the types and aspects
that contain them, is the primary way of classifying a node so it can be easily found via searches.
Metadata/Property Extractors
Some of the properties of a file node are set automatically when it is uploaded to the Repository,
such as author. This is handled by metadata extractors. A metadata extractor is set up to extract
properties from a specific file MIME type. There are numerous metadata extractors available out-
of-the-box covering common MIME types such as MS Office document types, PDFs, Emails,
JPEGs, HTML files, DWG files and more. The metadata extractors are implemented via the
Tika library, although custom metadata extractors are available. Each metadata extractor
implementation has a mapping between the properties it can extract from the content file, and
what content model properties that should be set as node metadata.
Node Associations
There are two types of associations:
• Parent to Child associations - these are for example folder to file associations where
deleting the folder will cascade delete its children.
• Peer to Peer - an example could be article to image associations where deleting the article
does not affect the related image node(s). These associations are also referred to as
source to target associations.

Developer Guide 63
Developer Guide

QName
All properties are defined within a specific content model, which also defines a unique
namespace. For example, a property called description can be part of many namespaces
(content models). To uniquely identify what description property is being referenced, a
fully qualified name, or a QName, is used. A QName has the following format: {namespace
URL}property local name, for example:

{http://www.alfresco.org/model/content/1.0}description

The first part in curly braces is the namespace identifier defining the content model that the
property is part of. The second part is the local name of the property (that is description in this
case).
A QName is used for types, aspects, properties, associations, constraints and so on. The
QName for the generic folder type that is part of the out-of-the-box document content model is
cm:folder. Note the use of cm to denote the namespace. Each content model defines a prefix for
each namespace that is used in the content model. Each type, aspect, property and so on in the
content model definition uses the namespace prefix instead of the full namespace URL. You will
also use the prefix when referring to entities such as types, aspects, properties, in the different
APIs.
Permissions
Permissions are set up per node and a node can inherit permissions from its parent node. A Role
(Group) Based Access Control configuration is the preferred way to set up permissions in the
repository. However, permissions can also be set for an individual user. Groups and users can be
synchronized with an external directory such as LDAP or MS Active Directory. Some groups are
created automatically during installation:
• EVERYONE – all users in the system
• ALFRESCO_ADMINISTRATORS – administrators with full access to everything in the
Repository.
• ALFRESCO_SEARCH_ADMINISTRATORS – can access the Search Manager tool and
set up search filters (facets).
• SITE_ADMINISTRATORS – can access the Site Manager tool and change visibility of
sites, delete sites, and perform site related operations.
• E-MAIL_CONTRIBUTORS – users that can send email with content into Alfresco Content
Services.
Permission settings involve three entities:

There are a number of out-of-the-box roles:


1. Consumer
2. Contributor
3. Editor
4. Collaborator
5. Coordinator
Whenever a Share site is created there are also four associated groups created that are used to
set up permissions within the site. In the repository, groups are prefixed with GROUP_ and roles
with ROLE_, this is important when referring to a group or role when using one of the APIs.

Developer Guide 64
Developer Guide

A Site is a collaboration area in Alfresco Share where a team of people can collaborate on
content.
Owner
The Repository contains a special authority called owner. Whoever creates a node in the
repository is called the owner of the node. Owner status overrides any other permission setting.
As owner you can do any operation on the node (basically the same as being coordinator/admin).
Anyone with Coordinator or Admin status can take ownership of a node (cm:ownable is then
applied).
Folder Node and File Node Overview
The diagram illustrates a typical folder node with a child file node when it has been classified with
the out-of-the-box default document content model:

Mini glossary
Terms and concepts used when developing for Alfresco Content Services.

Term Description

Actions Actions typically work in conjunction with Rules.


When creating a rule you specify the action to be
carried out when the rule is activated. There are
standard actions, but you can also create custom
actions. Custom actions are implemented in Java
as Spring beans.

Aspects While nodes must be of a single Type, they can


have multiple Aspects applied to them. Dublin
Core, Taggable, EXIF, Geographic, Emailed are
all examples of aspects. Also a single aspect can
be applied to multiple types.

Associations Relationships between Types are modeled


with Associations. There are two types: Child
Associations and Peer Associations.

Attributes Attributes provide a global means of storing key-


value data. Whereas properties are attached
to a node, attributes are system-wide, and not
stored per-node. They can be quickly searched for
without the need for an index and are cluster-safe.

Auditing Auditing allows you to track events that occur in


Alfresco Content Services. The events that you
audit are completely configurable.

Developer Guide 65
Developer Guide

Term Description

Configuration Platform provides many points at which the


configuration of the system can be changed. For
example, changes may be made to alfresco-
global.properties or many of the other
configuration files. In addition, Share is highly
configurable.

Content This is the piece of content to be stored in the


repository. It could be a Word document, a PDF, a
PNG image file, an audio file and so on. Note that
the content itself will be stored on the file system,
while its corresponding node, containing metadata,
will be stored in an RDBMS.

Content Model The content model describes the nodes and the
hierarchical relationship between them, as well
as any constraints that may exist. For example,
nodes that are of container type can contain other
nodes.

Content Renditions Renditions are manipulations of content that


typically involves some content transformation,
followed by some other operation such as crop
or resize. For example, you might have a PDF
document, which you might convert the first page
of to a PNG, and then crop and resize that image
to create a thumbnail view of the document. The
key service is the Rendition Service.

Content Store The repository has multiple content stores.


Typically this would include a main content store,
an encrypted content store, an archive content
store (for deleted items), and a version store (to
hold previous versions of documents). It is also
possible for developers to create custom content
stores for specific purposes.

Content Transformation Content transformation transforms one format


of content into another. There are numerous
applications of this, such as converting content
into plain text for indexing and generating
PDF versions of Word files. Transformations
can be chained together, for example DOC
to PDF using LibreOffice. Key service is the
ContentTransformation Service.

Extension Extensions can be thoughts of as server-side


additions to Alfresco Content Services. There
are two main types of extension: Platform and
Share. Each of these extension types are fully
described in this documentation, along with all
officially supported extension points.

Developer Guide 66
Developer Guide

Term Description

Events Data structures created on various changes within


the repository, such as name change of a piece of
content. There are three types of Event:
1. Inbound event - content arriving into a
folder
2. Outbound event - content leaving a folder
3. Update event - content being modified

Indexing As content is added to Alfresco Content Services


it is indexed by an indexer such as Solr. Solr
indexes both meta data and the plain text
content of files added. The content model
defines the metadata (aspects, properties,
types, associations) that are to be indexed via
the <tokenise> element. The indexes can
be queried using a variety of query languages,
including:
• fts-alfresco
• storeroot
• noderef
• xpath
• lucene
• cmis-strict
• cmis-alfresco
• db-afts
• db-cmis

Queries can be executed from JavaScript


and Java code, and also in the Node Browser
(available under Admin Tools in Share).

Nodes Each piece of content in the repository has a node


associated with it. The node contains information
about the content, such as its metadata and
location within the content store. The node is
stored in a RDBMS such as PostgreSQL, the
content itself is stored on the file system.

Predefined Content Model There are pre-defined content models provided


out-of-the-box, these include Folder/Document
hierarchy, Dublin Core, blogs, Wiki, Sites.

Policies These are event handlers triggered by certain


node events for either all nodes or just nodes of a
specific type.

Developer Guide 67
Developer Guide

Term Description

Metadata Most content has metadata associated with it. For


example, photographs have EXIF metadata. Word
documents would have Author, Creation Date,
and so on. The metadata provides very useful
information for document discovery, without the
overhead of having to extract and process the full
content of a document.

Metadata extraction Content type (mimetype) can automatically be


identified for the standard types by Tika. This
metadata can be extracted from the content
and copied into the content's associated node
(properties). For custom content types it is
possible to create Custom metadata extractors.
Key service is the MetadataExtractor Service. You
can also create custom metadata extractors.

Mimetypes The mimetype essentially identifies the type


of content. Alfresco Content Services can
automatically identify content types and establish
mimetype (using Tika). It is also possible to create
custom content identification through custom
mimetypes.

Module A module is the format in which an extension is


packaged.

Property Properties are named items of metadata


associated with a Type. Each Property is of
a specific data type (such as Text or Date).
Constraints can be applied to restrict the value of a
Property.

Repository This is where content is stored, and can be


thought of as the content stores and all the
related services. It consists of the filesystem or
storage service where the content is stored and
a database containing metadata. See Repository
Concepts for an overview.

Developer Guide 68
Developer Guide

Term Description

Rules Declarative definition of processes based on


addition, update, or removal of nodes with respect
to folders (think email rules for content). These are
set up for a folder in Share. See documentation
and videos on applying rules to folders. Note that
Rules can be filtered based on conditions/criteria:
• All items (no filter)
• Items with a specific mimetype (for example
.doc, .pdf)
• Contained in a category
• Specific content type applied to a specific
aspect file name pattern (for example *-
context.xml)

Boolean NOT can be used (for example not


.pdf). There are no limits to the number of
conditions that can be applied to each Rule.

Type A node is always of a single Type. A Type is


similar to a class in Object-Oriented Programming,
Types can be inherited from a parent Type in the
content model.

Developer Guide 69

You might also like