Skip navigation
All Places > Application Development Framework > Blog > Authors eugenio_romano

SIn the version 2.3.0 of ADF has been added a new Sidenav Layout Component : 


Sidenav Layout Component

In the next version of the generator this component will be by default present in your new generated app, but what if you want to add it to your current application?


In this tutorial, we are going to add this new component in a previous version of an ADF application. If you need more details about the sidenav layout component after the tutorial please check the documentation:

alfresco-ng2-components/ at master · Alfresco/alfresco-ng2-components · GitHub 


If you want to give a look at the example that I made, you can find it here:

Dev eromano sidebar by eromano · Pull Request #23 · Alfresco/adf-examples · GitHub 


Step 1


Update your current application package.json ADF dependencies to 2.3.0:


"@alfresco/adf-content-services" : "2.3.0"
"@alfresco/adf-process-services" : "2.3.0"
"@alfresco/adf-core" : "2.3.0"
"@alfresco/adf-insights" : "2.3.0"
"alfresco-js-api": "2.3.0"


Step 2


Generate the app-layout component:

ng generate component app-layout

Modify app-layout.component.html 


Note the route and the element in the HTML below are just for example, you need to feed this template with your own routes and elements:


<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="200" [stepOver]="780" [hideSidenav]="false" [expandedSidenav]="false">

        <ng-template let-toggleMenu="toggleMenu">
            <mat-toolbar color="primary" class="adf-app-layout-toolbar mat-elevation-z6">
                <button mat-icon-button (click)="toggleMenu()">

                <span fxFlex="1 1 auto" fxShow"true">ADF</span>

                <div class="adf-app-layout-menu-spacer"></div>


        <ng-template let-isMenuMinimized="isMenuMinimized">
            <mat-nav-list class="adf-sidenav-linklist">
                <a mat-list-item class="adf-sidenav-link" routerLink="/login">
                    <mat-icon matListIcon class="sidenav-menu-icon">vpn_key</mat-icon>
                    <div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">Login</div>
                <a mat-list-item class="adf-sidenav-link" routerLink="/apps">
                    <mat-icon matListIcon class="sidenav-menu-icon">device_hub</mat-icon>
                    <div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">Apps</div>
                <a mat-list-item class="adf-sidenav-link" routerLink="/documentlist">
                    <mat-icon matListIcon class="sidenav-menu-icon">folder_open</mat-icon>
                    <div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">Documents</div>
                <a mat-list-item adf-logout class="adf-sidenav-link">
                    <mat-icon matListIcon class="sidenav-menu-icon">exit_to_app</mat-icon>
                    <div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">Logout</div>



Modify app-layout.component.scss 


In this SCSS file should be present only the router outlet

adf-sidenav-layout {
    height: 100%;


Modify app.component.html 


In this HTML file should be present only the router outlet



Step 3


Modify app.routes.ts

Now let's add the AppLayoutComponent as the main route of the router and move all  yours previous route as children of the AppLayoutComponent as in this example:


export const appRoutes: Routes = [
    path: '',
    component: AppLayoutComponent,
    children: [
        path: '',
        component: HomeComponent


If you have more questions, please reply here or contact me using  gitter .

Often after you have to build your app for a different reason you need to track it.The analytics collected can be used for multiple purposes:

  • Track possible error in your software
  • New functionality engagement
  • Understand how the user interacts with your product


In my career, I saw multiple time create this kind of tracking systems implemented duplicating the code everywhere in the app. What I want to propose to you today is a simple way to archive this result taking advantages of the message log bus introduced in ADF 2.3.0. 

If you want to give a look at the example that I made, you can find it here:


logservice message bus example by eromano · Pull Request #22 · Alfresco/adf-examples · GitHub 


Step 1 Generate your application:


First, install yeoman:

npm install -g yo

Then the Alfresco Application Generator:

npm install -g generator-alfresco-adf-app

Then create the app

yo alfresco-adf-app

Step 2 create an account on Mixpanel


Go to and create your account. Once you have your account, you can get your Mixpanel token from the account information.


Step 3 add the log to your generated app


With the logService you can add differents kind of type of logs: 


Once you decided which kind of log is the one that you need to add a similar piece of code in your application:

import { LogService } from '@alfresco/adf-core';

export class AppComponent {

    constructor(logService: LogService) {
      this.logService.error('My error');
      this.logService.trace('My trace')
      this.logService.debug('My debug')'My info')
      this.logService.warn('My warn')

Please check the log service documentation for more details:  alfresco-ng2-components/ at master · Alfresco/alfresco-ng2-components · GitHub 


Step 4 Send the data to Mixapnel


In order to use Mixpanel we can take advantage of the Mixpanel javascript library:

Let's install it from npm:

npm install mixpanel

Now we need to redirect all the logService message to Mixpanel. The best way to archive this integration is adding a subscriber to the logService message bus:


import { Component } from '@angular/core';
import { LogService } from '@alfresco/adf-core';
import * as Mixpanel from 'mixpanel';

    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
export class AppComponent {

    constructor(public logService: LogService) {

        const mixpanel: any = Mixpanel.init('YOUR_MIXPANEL_TOKEN');
        mixpanel.set_config({ debug: true });

        logService.onMessage.subscribe((message) => {
            mixpanel.track(message.text, {
                type: message.type




If you have more questions, please reply here or contact me using  gitter .




Thi is my second article on how to use Docker with ADF. In the previous article ADF Docker practical guide I described how is possible start the demo shell or the example content app using Docker.

What if you want use docker in your generated ADF app? From the version 2.1.0 of our generator, we have added some new capability to help you in this task. Let's give a look together how to do it.



Generate an ADF app 

If you already have the version 2.1.0 or major of the alfresco generator app you can skip the two initial step.


  1. install Yeoman:
    npm install -g yo
  2. install lfresco Application Generator:
    npm install -g generator-alfresco-adf-app
  3. move to the folder where you want to create your project
    yo alfresco-adf-app
 After the  3 steps above, you should have your ADF scaffolder app ready.

If you need more information about our app generator please visit this repository: GitHub - Alfresco/generator-ng2-alfresco-app: Yeoman Generator Angular 2 Alfresco Application  

Let's test it! to start the app you need to run the command

Before npm start if you didn't select to install your dependencies. The command : "npm install" is necessary

npm start

Open in your browser http://localhost:4200/login to reach your generated app

Generate a Docker image

After generating the ADF app, you will find in your folder app a markdown file that will help you but don't worry, I will bring you through all the necessary steps:


Publish the generated app on docker hub

  1. First of all, if you do not have a Docker Hub account, you should create an account here:, the registration is absolutely free.
  2. From the folder of your generated app, build your app:
    npm run build
    The build command will create your dist folder

    If you want add some customization in the app.config.json you need to do it before the build

  3. Now you can build your Docker mage:
    docker image build -t myaccount/my_app_name:1.0 .
    Replace myaccount with your Docker Hub account name.
    Please note the ending "." symbol at the end of the command. It instructs the Docker to take current folder where the `Dockerfile` is located.
  4. .Is the moment to publish our newly created image in docker hub:

    docker push myaccount/my_app_name:1.0


Congratulations! at this point, your first app is on docker hub!

You can access your docker hub profile and even add some documentation to it visiting


Start the docker container

Now that your image is on docker hub you can quickly test and run it in any environment using the following command:

docker container run -p 80:80 --rm myaccount/my_app_name:1.0

--rm options will cleanup the container and image data once you stop the process.

At this point open in your browser http://localhost/login your running docker app!


Travis integration

All the generated app are provided with a Travis configuration file. This file can help you to initialize your CI on Travis.

if you what publish an image for any build of your ADF app you need to uncomment the relative part in the .travis.yml file:

#Uncomment this part if you want publish your docker image
#  - export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
#  - export TAG=`if [ "$BRANCH" == "master" ]; then echo "latest"; else echo $BRANCH ; fi`
#  - docker build -t $DOCKER_REPO:$TAG .
#  # Publish extra image based on Travis build number
#  - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
#  - docker push $DOCKER_REPO

The email, username, and password used for login should be stored in the repository settings environment variables, which may be set up through the web or locally via the Travis CLI, e.g.:

travis env set DOCKER_USERNAME myusername travis env set DOCKER_PASSWORD secretsecr

For more information see also Pushing a Docker Image to a Registry



When we created the generator our purpose was to give to all the users a full scaffolder to start an Angular project using the ADF framework.

if you have more questions or you need help to start to use those technologies, please reply here or contact me using gitter.


ADF Theming

Posted by eugenio_romano Employee Oct 11, 2017


ADF Theming


One of the first questions that I receive often when someone starts to play with ADF is how to customize the colors after generated an app.

In order to help all the developer to easily accomplish this task in the version 1.8.0 of ADF we have introduced a theming capability.



Before to completely dive on how to create your custom colors in ADF I would like to add a bit of context. ADF is a framework based on Angular and Material design.

Material design brings inside a lot of guidelines on how to style your applications and one of this guidelines is about the colors.

In Material Design there are mainly two colors to consider:

  • primary color refers to a color that appears most frequently in your app.
  • accent color refers to a color used to accent key parts of your UI. The accent should be used for the floating action button and interactive elements, such as : text fields and cursors, text selection, progress bars, selection controls, buttons, and sliders.

Starting from that two colors we fill in the spectrum to create a complete palette:


primary coloraccent color


All these different shades of primary/accent color are used to create a contrast between elements, you can use lighter or darker tones of your primary/accent color. 

If you want more information about Material design colors please refer to the official guidelines color documentation.

Now that we have a common understanding of how material design colors works let's give a look on how to customize it in ADF.


You can find this example in the folder belonging to the 1.9.0 projects in the ADF examples repo. 


1. Generation of an ADF project using the Yeoman ADF app generator 


Nothing to describe here. In case of our planned modifications, you should have a similar project, you can find the generator at this address


2. Choose the colors


If you search on google material design palette generator you will see a lot of results fell free to use the one that you like. I used this one Material Design Theme & Palette Color Generator . Once you have chosen the color that you like you will have something like that:



In order to use this colors we need to export it as Material2 palette:


$md-mycustomprimary: (
50 : #e8eaf6,
100 : #c5cae9,
200 : #9fa8da,
300 : #7986cb,
400 : #5c6bc0,
500 : #3f51b5,
600 : #3949ab,
700 : #303f9f,
800 : #283593,
900 : #1a237e,
A100 : #8c9eff,
A200 : #536dfe,
A400 : #3d5afe,
A700 : #304ffe,
contrast: (
50 : #000000,
100 : #000000,
200 : #000000,
300 : #ffffff,
400 : #ffffff,
500 : #ffffff,
600 : #ffffff,
700 : #ffffff,
800 : #ffffff,
900 : #ffffff,
A100 : #000000,
A200 : #ffffff,
A400 : #ffffff,
A700 : #ffffff,

$md-mycustomaccent: (
50 : #fdf3e3,
100 : #fbe1b8,
200 : #f9ce89,
300 : #f6ba59,
400 : #f4ab36,
500 : #f29c12,
600 : #f09410,
700 : #ee8a0d,
800 : #ec800a,
900 : #e86e05,
A100 : #ffffff,
A200 : #ffebdd,
A400 : #ffcdaa,
A700 : #ffbe91,
contrast: (
50 : #000000,
100 : #000000,
200 : #000000,
300 : #000000,
400 : #000000,
500 : #000000,
600 : #000000,
700 : #000000,
800 : #000000,
900 : #000000,
A100 : #000000,
A200 : #000000,
A400 : #000000,
A700 : #000000,


3. Change the theme.scss file


Copy and paste your palette colors variables in the  /src/custom-style.scss and use it in:

$primary: mat-palette($md-mycustomprimary);
$accent: mat-palette($md-mycustomaccent);
$warn: mat-palette($alfresco-warn);
$theme: mat-light-theme($primary, $accent, $warn);

@include angular-material-theme($theme);
@include adf-content-services-theme($theme);
@include adf-process-services-theme($theme);
@include adf-core-theme($theme);


now you can start your app and if everything goes ok you should have the following result:


light theme


4. Dark theme


A possible variation on the theming is creating a dark theme. Open the  /src/custom-style.scss  and replace the following line:


$theme: mat-light-theme($primary, $accent, $warn);


$theme: mat-dark-theme($primary, $accent, $warn);

Will generate the following result:


dark theme


Using a pre-built theme


Another possibility instead to create your custom colors uses the pre-built theme. ADF comes prepackaged with several pre-built theme CSS files. These theme files also include all of the styles for core (styles common to all components), so you only have to include a single CSS file for Angular Material in your app.

You can include a theme file directly into your application from ng2-alfresco-core/prebuilt-themes

Available pre-built themes:

  • adf-blue-orange.css
  • adf-blue-purple.css
  • adf-cyan-orange.css
  • adf-cyan-purple.css
  • adf-green-orange.css
  • adf-green-purple.css
  • adf-indigo-pink.css
  • adf-pink-bluegrey.css
  • adf-purple-green.css

If you're using Angular CLI you can include one of the prebuilt themes in your styles.css file:

@import '~ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css';

Or you can add it directly in your index.html

<link href="node_modules/ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css" rel="stylesheet">


If you want more information about theming you can check our official documentation and if you have more questions, please reply here or contact us using gitter.

Sentiment analysis


Before talking about code and architecture, some of you are probably wondering what sentiment analysis is. Generally speaking, sentiment analysis refers to the use of natural language processing in order to extract emotional states and subjective information from it.


The example presented in this blog is open source and you can find it here.

My goals during the creation of this example are:

  1. Integration between CS (Content services) and PS (Process services)
  2. Enable Sentiment Analysis in Alfresco
  3. Use ADF Angular framework to speed up the development


Because this example is also about the integration between CS and PS, you need to have both of them available. If you don't know how to do it, please take a look at this link.



Now a small architectural overview before drowning in the lines of code written for this example:




All the blocks inside the blue area are the FE (Front End) part made with ADF and the rest are BE (Back End).


In the FE we have:

  • ADF that provides all the necessary components (login, document-list, viewer, breadcrumbs, forms, etc..) to interact with CS and PS writing almost zero code.
  • alfresco-js-api which interacts between the CS and PS.
  • Sentiment component created for this post. You can find the related code here, but we will talk more about it below.  


In the BE we have:

  • Alfresco Content Service that will provide the text or the images for analysis.
  • A cognitive service (in this case, I used the Microsoft cognitive services but you can substitute the service layer with the services that you prefer).
  • Alfresco Process Service that allows us to execute actions such as moving the files into another folder depending on the results of the content sentiment analysis service.



As with all the ADF apps, generating the backbone of this app is quite easy. The only thing needed for this is the app generator. The app generator will allow you to have an FE application connected to the CS and PS in just a few minutes. As you can see, it's ready to be used with a login and document list.



After the generation of the app, we need to add our text and image analysis component.


When the user clicks on a file in the document list, the sentiment analysis component receives the content of the node. It then sends it to the sentiment analysis service. Once the async call returns with the results, it does two things:

  1. It shows the sentiment information, buzzwords and language recognition in a fancy, visual way.
  2. It saves the language sentiment and buzzword data in the node metadata.


Some useful links:

  • If you want to see the complete code which was used to create this app, please refer to this GitHub link.
  • To correctly configure the sentiment app, please refer to the instructions in the README of the project.


In order to make this blog as short as possible, I will only share the important part of the code. To see the whole solution, follow this link.


Text analysis

Our text analysis is split into two parts; the component and the service:

In the text-analysis.service.ts, we have all the calls against the sentiment analysis service and the content service: 

    sentimentByNodeId(nodeId) {
        return new Observable<any>(observer => {
            this.getContentNode(nodeId).subscribe((dataContent) => { //get the content from the content service with the nodeId

                this.sentiment(dataContent).subscribe((sentiment) => {

    sentiment(dataContent) {
        return new Observable<any>(observer => {
            let headers = new Headers({'Ocp-Apim-Subscription-Key': this.key}); // ... Set content type to JSON
            headers.append('Content-Type', 'application/json');
            let options = new RequestOptions({headers: headers}); // Create a request option

            let body = JSON.stringify(
                    'documents': [
                            'id': '1',
                            'text': dataContent

  `${this.urlService}sentiment`, body, options).subscribe((data) => {
            }, (err) => {

In the code above, we get the content of a node from the content service. We send the result to the sentiment analysis service in order to get the sentiment of the text in it.


Save sentiment info as metadata

We will store all the data collected from the cognitive API in the node metadata. This will later allow us to collect the metadata in our process and take some action based on the value of the sentiment metadata:  

    saveMetadata(language, keyPhrases, sentiment, nodeId) {
        let sentimentBoolean = sentiment >= 0.5;

        let properties = {
            'properties': {
                'mla:language': language,
                'mla:keyPhrases': JSON.stringify(keyPhrases),
                'mla:sentiment': sentimentBoolean,
                'cm:description': JSON.stringify(keyPhrases)

        this.apiService.getInstance().core.nodesApi.updateNode(nodeId, properties).then(function (data) {
            console.log('API called successfully. Returned data: ' + data);
        }, function (error) {

The sentiment app Screenshot




In the screenshot above, you can see the app which is running. When you click on the file bad.txt you can see the result of its sentiment analysis in the top right corner. Once the result is shown, it is also saved as metadata in the file. If you want to start the process, you have to add a Name and start it (See the bottom right of the screenshot).


PS Flow


For those of you who are familiar with the BPM world, the diagram below is almost self-explanatory:


  • The process starts getting a nodeId as an input.

  • In the second block, all the metadata related to this node is fetched from the content service through the API

  • If the sentiment is <0.5, the content will be moved to the "Bad Folder"
  • If the sentiment is >=0.5,  the content will be moved to the "Good Folder"

If you want to import this flow and try it locally, please refer to this other blog post.

After you have correctly set up the flow, the question is, how can I start this process from our ADF project?

Starting a process with ADF is really easy. I suggest you use the adf-start-process component. For more details about this component please see this link.

Let's see how to use it:



let inputProcessVariable = [];
let variable: any = {}; = 'nodeId';
variable.value = NODE_ID_CONTENT;



<adf-start-process appId="1" [variables]="inputProcessVariable" />


What have we just done with the code above? We have started our flow with our nodeId input. From this point forward, we don't need to do anything else. The process will analyze the sentiment metadata of the content using the given nodeId and move it into the right folder.



Integration with CS and PS is quite easy. You just need to input the right information into your process. With all the information in your hands in the Process Service, use the Content Service rest API to perform the actions that you need.

All the hype at the moment in software development is about AI. Sentiment analysis is one of the cool features that we can add but you can do much more. It is only a matter of finding the right way to integrate it into your project.

Feel free to contact us using gitter, the community portal, the webinars or any of the active channels Alfresco offers to get in touch.

As described in the getting started guide, a web client developed using the Application Development Framework (ADF) works on top of the Alfresco Content Services (ACS) and Alfresco Process Services (APS), and it is deployed on a independent web server. To enable the correct interaction between the three servers (mainly between the ADF web server and the ACS/APS servers) the so called cross-origin resource sharing (CORS) must be set up.


If you don't enable the CORS you may experience cross-domain requests, notably Ajax requests, forbidden by default by the same-origin security policy. In case you are experiencing a cross-domain request, in this post are shared multiple ways to solve the issue, with different approaches and solutions. Below the full list of the suggested solutions.


  1. Configuring webpack proxy
  2. Configuring angular-cli proxy
  3. Configuring nginx proxy
  4. Enabling CORS in ACS and APS

1. Configuring webpack proxy

In this solution you are going to use the bundled http-proxy-middleware of webpack, to proxy requests to a separate, possibly external, backend server.


Let's say we have an app running on http://localhost:3000/ and we want all calls redirect with the following strategy:


To develop the solution in this scenario, open the file config/webpack.common.jsfind the devServer section and add the following content:

devServer: {
        contentBase: helpers.root('dist'),
        compress: true,
        port: 3000,
        historyApiFallback: true,
        host: '',
        inline: true,
        proxy: {
            '/ecm': {
                target: {
                    host: "", // for windows
                    protocol: 'http:',
                    port: 8080
                pathRewrite: {
                    '^/ecm': ''
                secure: false,
                changeOrigin: true
            '/bpm': {
                target: {
                    host: "", // for windows
                    protocol: 'http:',
                    port: 9999
                pathRewrite: {
                    '^/bpm': ''
                secure: false,
                changeOrigin: true

Note if you are windows user you should use instead of

Last check if you generated the app with the ADF app Yeoman generator check that you have the right settings in your app: 

  1. Be sure that on the top left switch you have selected only the services that you are currently use.
  2. Be sure that on the top right settigs button you have the right proxy address:



  • With a different configuration of webpack the devServer properties could be defined in different files.

  • If you are running the App, content service or process service on different ports, change the ports accordingly to your local configuration. For further details about how to configure a webpack proxy please refer to the official documentation.  

2. Configuring angular-cli proxy

In this solution you are going to use angular-cli configurations to proxy requests to a separate, possibly external, backend server.


Let's say we have a app running on http://localhost:3000/ and we want redirect all the calls with the following strategy:

To develop the solution in this scenario, create a file next to projects package.json call it proxy.conf.json with the following content:

    "/ecm": {
                target: {
                    host: "",
                    protocol: "http:",
                    port: 8080
                pathRewrite: {
                    "^/ecm": ""
                secure: false,
                changeOrigin: true
    "/bpm": {
                target: {
                    host: "",
                    protocol: "http:",
                    port: 9999
                pathRewrite: {
                    "^/bpm": ""
                secure: false,
                changeOrigin: true

Note if you are windows user you should use instead of

run the following command to start the server with the proxy configuration:

ng serve --proxy-config proxy.conf.json --open

if you prefer you can also modify your start package.json to always use this configuration

"start": "ng serve --proxy-config proxy.conf.json --open",

if you are running the App, content service or process service on different ports, change the ports accordingly your local configuration. For further details about how to configure a webpack proxy please refer to the official documentation.

3. Configuring nginx proxy

In this solution you are going to use nginx [engine x] HTTP and reverse proxy server.

Installing nginx

Most Linux distributions come with nginx available to install via your package manager and on Mac OS you can use Homebrew. If you want to install manually however you can follow the instructions on the download page. See also the specific information for windows users.

Start nginx

Start nginx using the supplied configuration in nginx.conf.

nginx -c nginx.conf

Review nginx configuration

To correctly configure nginx use the following file nginx.conf. This will host Activiti, Alfresco and the app dev framework under the same origin.


To make everything work, you have to change the address of the ECM and BPM. In the demo app you can do that clicking on the top right settings menu and change the bottom left options: ECM host and BPM host.


This configuration assumes few things:

  • Port mapping:
    • nginx entry point:
    • Demo Shell: locathost:3000
    • Alfresco: locathost:8080
    • Activiti: locathost:9999

All those values can be modified at their respective location directive on the nginx.conf file.

If you want to know more on how to install and configure nginx to work with the Application Development Framework can be found here


4. Enabling CORS in Apache

Open the /conf/web.xml file from the apache folder and add this filter:



Note in the Configuration above you need to replace the URL_ALLOWED_ORIGIN placeholder with the right value. The is a list of origins that are allowed to access the resource. A * can be specified to enable access to resource from any origin(Not suggested configuration). Otherwise, a whitelist of comma separated origins can be provided. Eg:


For further information about how to configure a CORS filter in apache please refer to the official documentation Apache Tomcat 9 Configuration Reference (9.0.8) - Container Provided Filters 

5. Enabling CORS in ACS and APS

If you want completely enable CORS call on your Alfresco Content Services and Alfresco Process Services, please refer to the following alfresco documents: