The following diagram illustrates the three main components that the Alfresco Community Edition 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 Community Edition 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 Community Edition. 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 [1] section.
The Platform also contains numerous APIs [2], Services [3], and Protocols [4].
Alfresco provides a number of extension points to allow you to customize Alfresco Community Edition. These extensions points have various formats, but include:
The links in the list above provide further information on each of these topics.
The Alfresco Community Edition 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 Community Edition 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.
Alfresco Community Edition 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.
The content application server comprises a content repository and value-added services for building solutions.
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.
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.
The following diagram illustrates the platform architecture and extension points [24]. Note that this does not represent a complete list of extension points [24]:
The platform consists of the repository and all services, developer extension points [24], and APIs [2]. 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 [25].
Content models [25] 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 [26].
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 ReST API [27]. You can create new content models [25] that define new content types, metadata, and relationships. You can define custom actions [28] 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) [29] product.
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:
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:
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 Community Edition comes prepackaged with several content models. You can define new models for specific use cases from scratch or by inheriting definitions from existing models.
For more information see content model introduction [25].
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 Community Edition, 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 Community Edition and links to more detailed documentation.
Protocol | Description | Support Status |
---|---|---|
HTTP | The main protocol used to access the repository via for example the ReST APIs. | Standard in Alfresco Community Edition and Community Edition. |
WebDAV [31] | Web-based Distributed Authoring and Versioning is a 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. | Standard in Alfresco Community Editionand Community Edition. |
FTP [32] | File Transfer Protocol - standard network protocol for file upload, download and manipulation. Useful for bulk uploads and downloads. | Standard in Alfresco Community Edition and Community. |
Alfresco Office Services [33] | Alfresco Office Services (AOS) allow you to access Alfresco Community Edition directly from all your Microsoft Office applications. | Standard in Alfresco Community Edition and Community Edition. |
CMIS [34] | Alfresco fully implements both the CMIS [35] 1.0 and 1.1 standards to allow your application to manage content and metadata in an on-premise repository. | Standard in Alfresco Community Edition and Community Edition. |
IMAP [36] | Internet Message Access Protocol - allows access to email on a remote server. Alfresco Community Edition 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:
|
Standard in Alfresco Community Edition and Community Edition. |
SMTP [37] | It is possible to email content into the repository (InboundSMTP). A folder can be dedicated as an email target. | Standard in Alfresco Community Edition and Community Edition. |
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.
To support this approach, Alfresco Community Edition 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 Community Edition subsystem where a subsystem represents a complete coherent capability of the Alfresco Community Edition 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.
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.
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.
The Alfresco Application Development Framework [42], 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 [43] that can be used to build a content management web application and/or a process management web application.
For a complete list of all components with documentation see the ADF Component Catalogue [44].
ArchitectureThese 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 components talk to ADF services, which in turn talks to the Alfresco JS API [45], 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 GeneratorUsing the App Generator is simple. Install the Yeoman [47] 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 ❯ Alfresco Adf App ────────────── Update your generators Install a generator Find some help Clear global config
Select the 'Alfresco Adf App' generator and follow instructions.
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 [45].
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 [48], which are supported ways of injecting new custom code that should alter the functionality of the Share web application.
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:
The following sections get into a bit more details around Surf pages and Aikau pages.
The layout of a Share page is defined with the Surf development framework, which is a server side framework (Surf deep dive [49]). 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:
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:
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:
/WEB-INF/classes/alfresco /site-data /chrome /components ... global.header.xml /component-types /configurations /content-associations /extensions /page-associations /pages ... documentlibrary.xml ... 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 /themesThe Site Data model defines the page in XML [51], 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 [52] 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 [53] 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:
/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 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.
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:
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:
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 Community Edition, 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 [48] section (OOTB = Out-of-the-box functionality):
Extension Point Name |
---|
Share Configuration [56] |
Form Controls [57] |
Form Filters [58] |
Form Field Validation Handlers [59] |
Evaluators [60] |
Site Presets [61] |
Share Themes [62] |
Document Library [63] |
Surf Extension Modules [64] |
Surf Web Scripts [65] |
Surf Web Script JavaScript Root Objects [66] |
Surf Pages [67] |
Surf Dashlets [68] |
Surf Widgets [69] |
Aikau Menus [70] |
Aikau Pages [71] |
Aikau Dashlets [72] |
Aikau Widgets [73] |
Modifying OOTB Surf Pages [74] |
Modifying OOTB Surf Dashlets [75] |
Modifying OOTB Surf Widgets [76] |
Modifying OOTB Aikau Pages [77] |
Modifying OOTB Aikau Dashlets [78] |
Modifying OOTB Aikau Widgets [79] |
Modifying OOTB Surf Web Scripts [80] |
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:
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.
<?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). 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.
<?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!
page.helloworldhome.title=Hello World page.helloworldhome.description=Hello World Home DescriptionThis 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:
<#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"> <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:
<#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 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:
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 [55].
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:
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 [6] 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> <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>
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 Community Edition and then access the page with the http://localhost:8080/share/page/hdp/ws/helloworld URL. You should see the following page in Share:
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.
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.
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 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.
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 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.
The end-to-end rendering flow is illustrated in the following figure.
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.
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.
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:
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.
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.
You can define views of pages including specific pages, page formats, and page types.
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.
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.
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.
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=wirelesshttp://localhost:8080/surf?pt=login&f=wireless
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 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: <@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.
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:
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 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:
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 Community Edition 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.
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:
<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.
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. 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.
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 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> <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.
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 Community Edition-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.
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 Community Edition 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 Community Edition 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 Community Edition 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 Community Edition instance. All you need to do is define an endpoint that points to an Alfresco Community Edition instance and uses the alfresco connector. Alfresco Community Edition 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>
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 Community Edition 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.
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.:
In the following sections you will see that two locations are specified:
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 Community Edition objects would be located in alfresco/site-data directory, and third-party overrides/extensions would be located in alfresco/web-extension/site-data.
<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>
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>
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>
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>
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> </component>
<configuration> <source-id>SOURCE_ID</source-id> </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>
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).
<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>
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>
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.
<template-instance> <template-type>TEMPLATE_TYPE_ID</template-type> </template-instance>
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>
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.
<template-type> <!-- Required "view" processor --> <processor mode="view"> <id>PROCESSOR_ID</id> <!-- Optional Uri --> <uri>PROCESSOR_URI</uri> </template-type>
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>
A theme captures default settings for the rendering framework. Different themes can have different rendering behaviors.
In the following sections you will see that two locations are specified:
<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>
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>
Specific information:
One of the problems that has affected upgrades of Alfresco Community Edition in the past is that the end-user's browser can end up using cached copies of JavaScript and CSS files that have 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>
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.
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:
The production suffices are:
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.
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).
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.
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> ... <generate-css-data-images>true</generate-css-data-images> ... </web-framework>
Providing that checksum dependencies are also enabled [112] 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.
This information expands on FreeMarker template attributes and directives.
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>
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".
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:
<script type="text/javascript" src="/share/res/A.js"></script> <script type="text/javascript">//<![CDATA[ // A comment between imports //]]></script> <script type="text/javascript" src="/share/res/B.js"></script>
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.
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 Community Edition.
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>
If you do enable dependency aggregation then you can expect the following behaviour to occur. If the file A.js contains:
var a = 1;
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.
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.
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.
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.
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 Alfresco ReST API [27], 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 CMIS ReST API [34], 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 [113] and a Repository JavaScript API [114]. 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 page [2].
Introduction
The way you deploy and run the Alfresco Community Edition 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 Community Edition from a number of Docker images. These images are available in the https://hub.docker.com [115] 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 Community Edition. 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 Community Edition 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.
Docker Architecture
When Alfresco Community Edition is deployed with Docker it looks something like this:
To build the alfresco/alfresco-content-repository-community image Alfresco uses the https://github.com/Alfresco/acs-community-packaging [116] 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 like in order to run with different versions than those set by default, which are usually the latest versions.
Kubernetes Architecture
Alfresco Community Edition cluster deployments are managed via Kubernetes container orchestrator. The deployment architecture is defined with the Helm tool.
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.
Alfresco Community Edition 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.
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:
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 Community Edition are stored in what is referred to as the repository. The repository is a logical entity that consists of three important parts:
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 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).
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 Community Edition extension modules.
Content Store Implementation
When considering file storage, it should be noted that files added to Alfresco Community Edition 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:
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.
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:
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:
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:
Permission settings involve three entities:
There are a number of out-of-the-box roles:
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:
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 Community Edition. The events that you audit are completely configurable. |
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 [118]. |
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 Community Edition. 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. |
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:
|
Indexing | As content is added to Alfresco Community Edition 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:
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. |
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 [119]. |
Mimetypes | The mimetype essentially identifies the type of content. Alfresco Community Edition 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 [1] for an overview. |
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 [120]. Note that Rules can be filtered based on conditions/criteria:
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. |
Links:
[1] https://docs.alfresco.com/dev-repository-concepts.html
[2] https://docs.alfresco.com/dev-api-intro.html
[3] https://docs.alfresco.com/dev-services.html
[4] https://docs.alfresco.com/dev-protocols.html
[5] https://docs.alfresco.com/dev-platform-arch.html
[6] https://docs.alfresco.com/dev-extensions-share-architecture-extension-points.html
[7] https://docs.alfresco.com/dev-platform-integration-arch.html
[8] https://docs.alfresco.com/../concepts/alfresco-principles.html
[9] https://docs.alfresco.com/../concepts/alfresco-arch-about.html
[10] https://docs.alfresco.com/../concepts/acs-deploy-architecture.html
[11] https://docs.alfresco.com/../concepts/deploy-options.html
[12] https://docs.alfresco.com/../concepts/models-about.html
[13] https://docs.alfresco.com/../concepts/dev-repository-concepts.html
[14] https://docs.alfresco.com/../concepts/dev-glossary.html
[15] https://docs.alfresco.com/../concepts/dev-for-developers.html
[16] https://docs.alfresco.com/../concepts/dev-arch-overview.html
[17] https://docs.alfresco.com/../concepts/dev-system-arch-platform.html
[18] https://docs.alfresco.com/../concepts/dev-system-arch-web-ui.html
[19] https://docs.alfresco.com/../concepts/api-about.html
[20] https://docs.alfresco.com/../concepts/dev-platform-arch.html
[21] https://docs.alfresco.com/../concepts/content-modeling-about.html
[22] https://docs.alfresco.com/../concepts/dev-protocols.html
[23] https://docs.alfresco.com/../concepts/spring-fw-modularity.html
[24] https://docs.alfresco.com/dev-platform-extension-points.html
[25] https://docs.alfresco.com/../references/dev-extension-points-content-model.html
[26] http://dev.alfresco.com/resource/AlfrescoOne/5.0/PublicAPI/
[27] https://docs.alfresco.com/../pra/1/topics/pra-welcome.html
[28] https://docs.alfresco.com/../references/dev-extension-points-actions.html
[29] https://docs.alfresco.com/process-services1.10/topics/developmentGuide.html
[30] https://docs.alfresco.com/admintools-cmm-intro.html
[31] https://docs.alfresco.com/troubleshoot-webdav.html
[32] https://docs.alfresco.com/fileserv-ftp-intro.html
[33] https://docs.alfresco.com/aos/concepts/aos-intro.html
[34] https://docs.alfresco.com/../pra/1/topics/cmis-welcome.html
[35] https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=cmis
[36] https://docs.alfresco.com/imap-intro.html
[37] https://docs.alfresco.com/email-intro.html
[38] https://docs.alfresco.com/../concepts/dev-system-arch-web-ui-app-dev.html
[39] https://docs.alfresco.com/../concepts/dev-extensions-share-architecture-extension-points.html
[40] https://docs.alfresco.com/../concepts/dev-system-arch-web-ui-app-dev-adf.html
[41] https://docs.alfresco.com/../concepts/dev-system-arch-web-ui-app-dev-js-api.html
[42] https://github.com/Alfresco/alfresco-ng2-components
[43] https://alfresco.github.io/adf-component-catalog/
[44] https://alfresco.github.io/adf-component-catalog
[45] https://github.com/Alfresco/alfresco-js-api
[46] https://github.com/Alfresco/generator-alfresco-adf-app
[47] https://yeoman.io
[48] https://docs.alfresco.com/dev-extensions-share-extension-points-introduction.html
[49] https://docs.alfresco.com/surf-fwork-intro.html
[50] https://docs.alfresco.com/../references/surf-object-xml-reference.html
[51] https://docs.alfresco.com/../references/surf-object-xml-reference-page.html
[52] https://docs.alfresco.com/../references/surf-object-xml-reference-component.html
[53] https://docs.alfresco.com/../references/surf-object-xml-reference-template-instance.html
[54] https://docs.alfresco.com/dev-extensions-share-architecture-extension-points-intro-surf-pages.html
[55] https://docs.alfresco.com/dev-extensions-share-architecture-extension-points-intro-aikau-pages.html
[56] https://docs.alfresco.com/dev-extensions-share-configuration.html
[57] https://docs.alfresco.com/dev-extensions-share-form-controls.html
[58] https://docs.alfresco.com/dev-extensions-share-form-filters.html
[59] https://docs.alfresco.com/dev-extensions-share-form-field-validation-handlers.html
[60] https://docs.alfresco.com/dev-extensions-share-evaluators.html
[61] https://docs.alfresco.com/dev-extensions-share-site-presets.html
[62] https://docs.alfresco.com/dev-extensions-share-themes.html
[63] https://docs.alfresco.com/dev-extensions-share-doclib-actions.html
[64] https://docs.alfresco.com/dev-extensions-share-surf-extension-modules.html
[65] https://docs.alfresco.com/dev-extensions-share-surf-web-scripts.html
[66] https://docs.alfresco.com/dev-extensions-share-surf-web-script-js-root-objects.html
[67] https://docs.alfresco.com/dev-extensions-share-surf-pages.html
[68] https://docs.alfresco.com/dev-extensions-share-surf-dashlets.html
[69] https://docs.alfresco.com/dev-extensions-share-surf-widgets.html
[70] https://docs.alfresco.com/dev-extensions-share-aikau-menus.html
[71] https://docs.alfresco.com/dev-extensions-share-aikau-pages.html
[72] https://docs.alfresco.com/dev-extensions-share-aikau-dashlets.html
[73] https://docs.alfresco.com/dev-extensions-share-aikau-widgets.html
[74] https://docs.alfresco.com/dev-extensions-share-override-ootb-surf-pages.html
[75] https://docs.alfresco.com/dev-extensions-share-override-ootb-surf-dashlets.html
[76] https://docs.alfresco.com/dev-extensions-share-override-ootb-surf-widgets.html
[77] https://docs.alfresco.com/dev-extensions-share-override-ootb-aikau-pages.html
[78] https://docs.alfresco.com/dev-extensions-share-override-ootb-aikau-dashlets.html
[79] https://docs.alfresco.com/dev-extensions-share-override-ootb-aikau-widgets.html
[80] https://docs.alfresco.com/dev-extensions-share-override-ootb-surf-webscripts.html
[81] https://docs.alfresco.com/../concepts/dev-extensions-share-architecture-extension-points-intro-surf-pages.html
[82] https://docs.alfresco.com/../concepts/dev-extensions-share-architecture-extension-points-intro-aikau-pages.html
[83] https://docs.alfresco.com/../concepts/surf-fwork-intro.html
[84] https://docs.alfresco.com/../concepts/dev-extensions-share-advanced-surf-topics.html
[85] http://spring.io/
[86] http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
[87] https://dojotoolkit.org/
[88] https://github.com/dojo/dijit
[89] https://docs.alfresco.com/../concepts/surf-types-of-content%28replace-surf-concepts%29.html
[90] https://docs.alfresco.com/../concepts/surf-content-delivery.html
[91] https://docs.alfresco.com/../concepts/surf-mvc.html
[92] https://docs.alfresco.com/../concepts/spring-web-mvc.html
[93] https://docs.alfresco.com/../concepts/surf-view-fwork-intro.html
[94] https://docs.alfresco.com/../concepts/surf-pres-content.html
[95] https://docs.alfresco.com/../concepts/surf-connectors-intro.html
[96] https://docs.alfresco.com/../tasks/surf-page-view.html
[97] https://docs.alfresco.com/../concepts/surf-templates-regions.html
[98] https://docs.alfresco.com/../concepts/surf-components.html
[99] https://docs.alfresco.com/../concepts/surf-objects.html
[100] https://docs.alfresco.com/../concepts/surf-templates.html
[101] https://docs.alfresco.com/../concepts/surf-webscripts.html
[102] https://docs.alfresco.com/../concepts/surf-connectors-endpoints.html
[103] https://docs.alfresco.com/../concepts/surf-credentials.html
[104] https://docs.alfresco.com/../concepts/surf-authenticators.html
[105] https://docs.alfresco.com/../concepts/surf-remote-api.html
[106] https://docs.alfresco.com/../references/surf-object-xml-reference-configuration.html
[107] https://docs.alfresco.com/../references/surf-object-xml-reference-template-type.html
[108] https://docs.alfresco.com/../references/surf-object-xml-reference-theme.html
[109] https://docs.alfresco.com/../concepts/dev-extensions-share-surf-checksums.html
[110] https://docs.alfresco.com/../concepts/dev-extensions-share-css-data-image-support.html
[111] https://docs.alfresco.com/../concepts/dev-extensions-share-aggregate-dependencies.html
[112] https://docs.alfresco.com/dev-extensions-share-surf-checksums.html
[113] https://docs.alfresco.com/java-public-api-list.html
[114] https://docs.alfresco.com/API-JS-intro.html
[115] https://hub.docker.com
[116] https://github.com/Alfresco/acs-community-packaging
[117] https://docs.alfresco.com/dev-system-arch-web-ui-app-dev.html
[118] https://docs.alfresco.com/share-configuring-intro.html
[119] https://docs.alfresco.com/../references/dev-extension-points-custom-metadata-extractor.html
[120] https://docs.alfresco.com/library-folder-rules.html