UI Extensions

The Docker-based Cloud CMS UI provides additional extension patterns beyond the AMD-driven mechanism for user
interface components and screens. Since Docker allows you to run on-premise, you can use these extension patterns
to influence more foundational changes to the way the application works.

Environment Variables

When the Cloud CMS user interface starts up, it looks to environment variables to tell it whether there are any
extensions available to be loaded. Extensions are stored with an extensions directory (referred to as /ext)
which contain a JSON descriptor file and any assets.

CLOUDCMS_UI_EXT_PATH

The CLOUDCMS_UI_EXT_PATH environment variable tells the Cloud CMS UI the location of your extensions directory.
This can be a relative path (defined relative to the root directory of the Cloud CMS UI application) or it can be
an absolute path somewhere on disk.

Examples:

CLOUDCMS_UI_EXT_PATH=~/projects/cloudcms/ui/ext
CLOUDCMS_UI_EXT_PATH=/ui/ext
CLOUDCMS_UI_EXT_PATH=./ext

CLOUDCMS_UI_EXT_REPLACE

The CLOUDCMS_UI_EXT_REPLACE environment variable specifies whether you would like your extensions to completely
replace the out-of-the-box configurations (true) or whether you would like them to merge/override (false).

The default behavior is to merge/override.

Within your JSON configuration, scalar values like strings and integers are overridden.
Arrays and objects are merged.

Installing into Docker

There are two ways to introduce your extensions into Docker.

Option #1: Using a Volume Mapping

The first way is to keep your extensions on disk locally and map them into the Docker container. This is great
for development since it lets you work locally within your IDE and have your changes reflected right away inside
the container.

Here's how to do it:

  1. Add the following environment variables to your ui.env file:

    CLOUDCMS_UI_EXT_PATH=./ext
    CLOUDCMS_UI_EXT_REPLACE=true

  2. Edit your docker-compose.yml file for the UI service and map a volume to your development directory:

For example:

  ui:
    build: ./ui
    networks:
      - cloudcms
    depends_on:
      - api
    env_file:
      - ./ui/ui.env
    ports:
      - "80:80"
    volumes:
      - /mydevfolder/ext:/var/app/current/ext

Note that /var/app/current is the application root. Thus, we're mapping your local extensions files to the
./ext path relative to the application root. This is where the CLOUDCMS_UI_EXT_PATH variable points.

Option #2: Using a Build

The second way is to copy your extensions into the ui service's build before running. This is great for
production purposes or for deploying to Docker Machines outside of your laptop (or local computer). For example,
if you wanted to deploy to an Amazon EC2 host using Docker Machine's EC2 driver, this is the way to go.

Here's how to do it:

  1. Add the following environment variables to your ui.env file:

    CLOUDCMS_UI_EXT_PATH=./ext
    CLOUDCMS_UI_EXT_REPLACE=true

Note that this is the same as in Option #1.

  1. Copy your local extension directory to the UI services directory.

For example,

cp -r /mydevfolder/ext ./ui
  1. Modify the UI service's Dockerfile and add the following:

    COPY ext /var/app/current/ext

When you now run docker-compose build --force-rm, the UI service will be built and it will copy everything
in the UI service's ext folder to the /var/app/current/ext path off the application root.

At the moment, Docker Compose does not allow you to COPY contents to the Docker container from outside of the
service folder. As such, step #2 is cumbersome, but necessary. You may be able to utilize an alias or a script
to help you to run these steps in tandem as part of a build process.

Example: Arrested Development

The Cloud CMS SDK includes sample extensions that enhance the user interface to match Netflix's hit show
Arrested Development. The television show is
a lot of fun but we mostly just use it as a means to do some fun theming.

The SDK sample is available here:

[https://github.com/gitana/sdk/tree/master/ui/arrested-development-ext](https://github.com/gitana/sdk/tree/master/ui/arrested-development-ext)

We recommend cloning this repository and inspecting the files contained therein to get a sense of what is possible.

To get started, use Option #1 and do the following:

  1. Add the following environment variables to your ui.env file:

    CLOUDCMS_UI_EXT_PATH=./ext
    CLOUDCMS_UI_EXT_REPLACE=true

  2. Edit your docker-compose.yml file and add:

    volumes:
      - /<sdkPath>/ui/arrested-development-ext:/var/app/current/ext

Where sdkPath is the path to your SDK root directory on disk

/ext Folder Structure

The folder structure should look like this:

custom-application.json
web
    ...any web assets
services
    ...any custom services
    

Any files under web will be accessible via the /ext/<path> URI, allowing you to includes images, custom
JavaScript, CSS and anything else you'd like.

The custom-application.json file informs the Cloud CMS UI application dispatcher about how to render the
front splash page as well as what HTML to inject into the index.html file that renders.

Here is an example of how the JSON can look:

{
    "app": {
        "title": "Cloud CMS"
    },
    "splash": {
        "message": "One moment while the application loads...",
        "logo": {
            "id": "logo",
            "enabled": true,
            "config": {
                "url": "/splash/cloudcms-splash-logo.png"
            }
        },
        "spinner": {
            "id": "spinner-three-bounce",
            "enabled": true,
            "config": {}
        },
        "quotes": [{
            "quote": "It ain’t about how hard you can hit. It’s about how hard you can get hit and keep moving forward; how much you can take and keep moving forward. That’s how winning is done.",
            "author": "Rocky Balboa"
        }, {
            "quote": "Leaders are made, they are not born. They are made by hard effort, which is the price which all of us must pay to achieve any goal that is worthwhile.",
            "author": "Vince Lombardi"
        }, {
            "quote": "Some luck lies in not getting what you thought you wanted but getting what you have, which once you have got it you may be smart enough to see is what you would have wanted had you known.",
            "author": "Garrison Keillor"
        }, {
            "quote": "Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work. And the only way to do great work is to love what you do.",
            "author": "Steve Jobs"
        }, {
            "quote": "Usually, in the studio, you just go out and have a play over it, and see what comes, and it's usually - mostly - the first take that's the best one.  And you find yourself repeating yourself thereafter.",
            "author": "David Gilmour"
        }, {
            "quote": "Our greatest weakness lies in giving up. The most certain way to succeed is always to try just one more time.",
            "author": "Thomas Edison"
        }, {
            "quote": "Imagination will often carry us to worlds that never were. But without it we go nowhere.",
            "author": "Carl Sagan"
        }, {
            "quote": "Things are never quite as scary when you've got a best friend.",
            "author": "Bill Watterson"
        }, {
            "quote": "All we need to do is keep talking.",
            "author": "Stephen Hawking"
        }, {
            "quote": "Actual happiness always looks pretty squalid in comparison with the overcompensations for misery.",
            "author": "Aldous Huxley"
        }, {
            "quote": "To believe your own thought, to believe that what is true for you in your private heart is true for all men — that is genius.",
            "author": "Ralph Waldo Emerson"
        }, {
            "quote": "Can't keep my eyes from the circling skies... tongue-tied and twisted, just an earth-bound misfit, I.",
            "author": "Pink Floyd"
        }]
    },
    "footer": {
        "logo": "<a href='https://gitana.io' target='_blank'><div class='cloudcms-theme-footer'></div></a>",
        "version": "<p class='cloudcms-oneteam-version-tag'></p>",
        "copyright": "<p class='copyright-text'>Copyright &copy; 2017 <a href='https://gitana.io' target='_blank'>Gitana Software, Inc.</a> | All Rights Reserved</p>"
    },
    "services": []
}

The app section describes general properties about the application.

The splash section describes how to render the initial Splash or loading page. The splash page is shown right
away while the rest of the application is loading. The application itself is AMD-based which means that it
lazy-loads modules as needed. The user is asked to wait while the initial module set loads and the splash page
is shown in the mean time.

The logo section provides markup that appears at the bottom of the page. Essentially, the footer is comprised
of a logo on top followed by a version tag and a copyright. You simply provide HTML for these blocks or leave as
an empty string to blank out that section.

The services section allows to describe HTML blocks that should be injected into the HTML <head> of the
index.html page.

Services

Services let you inject HTML into the user interface. You can do this in two places:

  • within the logo and spinner blocks for the Splash page
  • within the services array for the overall HTML page's `'.

Each service consists of a Handlebars template that receives a config block to render.

The following out-of-the-box services are available:

google-analytics

This renders the JavaScript code for Google Analytics.

Example:

{
    "id": "google-analytics",
    "enabled": true,
    "config": {
        "id": "UA-12345678-9",
        "domain": "mydomain.com"
    }
}

logo

This renders a logo image onto the page.

Example:

{
    "id": "logo",
    "enabled": true,
    "config": {
        "url": "/ext/banana-stand.png"
    }
}

spinner-three-bounce

This renders a "please wait" spinner with three bouncing balls.

Example:

{
    "spinner": {
        "id": "spinner-three-bounce",
        "enabled": true,
        "config": {}
    }
}

spinner-circle

This renders a "please wait" spinner with a rotating circle.

Example:

{
    "spinner": {
        "id": "spinner-circle",
        "enabled": true,
        "config": {}
    }
}

css

This injects custom CSS into the page.

Example:

{
    "id": "css",
    "enabled": true,
    "config": {
        "urls": [
            "/ext/custom.css"
        ]
    }
}

favicon

This injects the markup into the page to control the favicon.

Example:

{
    "id": "favicon",
    "enabled": true,
    "config": {
        "url": "/ext/custom-favicon.png"
    }
}

Build your own services

You can build your own services by adding HTML files to your extension source services directory.

Example: audio

In the Arrested Development SDK example, we've added an audio.html file under services.
The file is really simple and looks like this:

<script>
    var a = new Audio("");
    a.play();
</script>

It uses the config.url variable to load up an audio file and play it.

We can therefore configure our index.html page to use it by adding a services block like this:

{
    "id": "audio",
    "enabled": true,
    "config": {
        "url": [
            "/ext/arrested-development.mp3"
        ]
    }
}

When the Cloud CMS UI loads, the <script> will be injected into the page and the Arrested Development
theme music will play. Odd, yes. But demonstrative of how you can extend the UI.