Hello Magnolia - Headless

In this introductory tutorial, we are going to:

Scenario

We received the UX from our design team, but they still haven’t decided on the details yet. However the content from our travel packages is already defined and we do not want to hold our marketers back from writing their content.

Install the demo project

To jumpstart our project, we will install our demo project. The easiest way to do this is to clone our git repository.

  1. Create a directory named basic-headless-demos and cd to that directory.

    mkdir basic-headless-demos;
    cd basic-headless-demos;
  2. Clone the git repository.

    git clone hello-magnolia-headless https://git.magnolia-cms.com/scm/demos/basic-headless-demos.git

Install Magnolia

  1. Switch to the basic-headless-demos folder:

    cd basic-headless-demos
  2. Change directory to magnolia.

    cd magnolia
  3. Start magnolia.

    mgnl jumpstart
  4. Choose number 2 (magnolia-community-webapp)

    ? What Magnolia would you like to install?
      1) magnolia-empty-webapp
      2) magnolia-community-webapp (1)
      3) magnolia-community-demo-webapp
      4) magnolia-dx-core-webapp
      5) magnolia-dx-core-demo-webapp
      Answer: 2
    1 This one!

Once the installation is finished you will see an output similar to this on your console:

info Magnolia has been successfully setup for light development!
info You can now open a new terminal tab or window and start it up with the CLI command 'mgnl start'
info Magnolia will be ready after a few seconds at localhost:8080/magnoliaAuthor. Username and password is superuser
install magnolia cli

Before starting

Before you get going too far, pick up some handy configuration and sample images.

  1. From the magnolia directory, run:

    • Mac or Linux

    • Windows

    cp -rv ./_dev/content-to-bootstrap/* ./apache-tomcat/webapps/magnoliaAuthor/WEB-INF/bootstrap/common (1)
    xcopy ./_dev/content-to-bootstrap ./apache-tomcat/webapps/magnoliaAuthor/WEB-INF/bootstrap/common\ /E/H (1)
    1 This copies all files inside /magnolia/_dev/content-to-bootstrap into /magnolia/apache-tomcat/webapps/magnoliaAuthor/WEB-INF/bootstrap/common.
  2. Start Magnolia

    mgnl start

What are we going to build?

We are going to build a Travel Website SPA promoting travel packages. - Marketers can add new Navigation Items - They can create new Pages and arrange the content freely with pre-defined Content Blocks

What is a Headless CMS?

When talking about Headless CMS we are typically talking about something we call ``Form-Centric or API first Approach''.

You want to create a technology and layout agnostic way for marketers to create content, and for developers to consume the content and use in any way they want, and whatever tools they want.

What will we do next?
  • Create our Content Type

  • Create a Magnolia App

  • Defining our REST API Endpoint

Defining our Content Type

Scenario

After talking to the design and marketing team we know that our Travel Packages can have the following information: - name - description - featured - image - tour types - location - date - duration - tour operator - body, some rich text to describe the travel package.

What we have just described is our Content Model. It’s the contract between developers and marketers to create their website.

With Magnolia CMS we describe the model of content with something we call ``Content Types''. Implementing content types is very developer friendly, just describe the type in a Yaml file.

  1. In our hello-magnolia-headless folder we will go into contentTypes directory:

    cd ./basic-headless-demos/magnolia/light-modules/basic-tours-lm/contentTypes
  2. Create a file named tours.yaml with the following content:

    tours.yaml

    datasource:
        workspace: tours
        autoCreate: true
    
    model:
      properties:
        name:
          label: Name
          required: true
          i18n: true
    
        description:
          label: Description
          i18n: true
    
        isFeatured:
          type: Boolean #Types 'Decimal', 'Long' and 'Double' are also available.
          label: Feature this item
    
        image:
          type: asset
          label: Image
    
        tourTypes:
          label: Tour Types
          type: reference:category
          multiple: true
    
        location:
          label: Start City
          i18n: true
    
        date:
          label: Date
          type: Date
    
        duration:
          type: Long
          label: Tour Duration
          options:
            "2":
              value: 2
              label: 2 days
            "7":
              value: 7
              label: 7 days
            "14":
              value: 14
              label: 14 days
            "21":
              value: 21
              label: 21 days
    
        tourOperator:
          label: Tour Operator
          i18n: true
    
        body:
          type: richText
          label: Body
          i18n: true

    create content type

Magnolia will automatically pickup the new content type and will make it available on our running instance.
Check out Content Types for more details.

Create a Magnolia App

  1. Change to the basic-tours-lm/apps directory.

    cd ../apps
  2. Create a file named tours.yaml with following content:

    !content-type:tours
    name: tours
    label: Tours
    
    # Optionally override any of the app configuration supplied by the content type.
    subApps:
      detail:
        label: Tour Detail
        form:
          properties:
            isFeatured:
              buttonLabel: Featured
To see your new Tours app, log out and log back in, and press the grid icon.

Install content

We have prepared Demo Content to start with. To install the Demo Content, follow these steps:

  • Open the Tours app.

  • Click Import action (You might need to scroll down in the action bar.)

  • Select the file ./magnolia/_dev/content-to-import/tours.magnolia-travels.xml

import tours content

Browsing our content

These steps were already enough to browse the content and to use the Form Editor.

02 01 browse

What did we just do?

  • We modeled our content and created a Content Type

  • We created a ``Tours'' App

  • We installed Demo Content

  • We browsed through the Tours App and viewed Demo Content

Defining our REST API Endpoint

Defining our REST API Endpoints is as easy as creating the content type and app.

  1. Change to the basic-tours-lm/restEndpoints/delivery directory.

    cd ../restEndpoints/delivery
  2. Create a file named (you guessed it) tours.yaml with the following content:

    class: info.magnolia.rest.delivery.jcr.v2.JcrDeliveryEndpointDefinition
    workspace: tours
    depth: 10
    bypassWorkspaceAcls: true
    includeSystemProperties: true
    nodeTypes:
      - mgnl:content
    
    references:
      - name: assetReference
        propertyName: image
        referenceResolver:
          class: info.magnolia.rest.reference.dam.AssetReferenceResolverDefinition
          assetRenditions:
            - 960x720
            - 480x360
    Congratulations!

    We are now ready to see our app in action.

Accessing our content

  • The limit query parameter. Endpoints typically limit results to 10 results by default.

  • The orderBy parameter. You can order on any property.

02 01 browse json

Extra: Updating and adding new Tours

What did we just do?

  • We have installed a Headless CMS

  • We have imported Demo Content

  • We have changed existing and added new content

  • Without any coding we can see the json to build our website

Extra: Build a client

We have seen how easy it is to get started. Let’s build our website to consume the data. We will build a very simple project.

02 01 vanilla result

Open the basic-headless-demos/frontend directory.

Write the client

Edit index.html and copy in the following code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"/>
    <title>Hello, world!</title>
  </head>
  <body>
    <div class="jumbotron">
      <h1 class="display-4">Magnolia Tours</h1>
      <p class="lead">List of all Tours</p>
    </div>
    <div class="container">
      <div class="card-columns tours"><!-- placeholder for results --></div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.8.3/polyfill.min.js" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script>
      (function($) {
        const ENDPOINT = "http://localhost:8080/magnoliaAuthor/.rest/delivery/tours/?limit=100&orderBy=mgnl:lastModified%20desc";
        const IMAGE_BASE = 'http://localhost:8080';

        $.get(ENDPOINT, function(data) {
          $(".tours").append(data.results.map((item)=>`
                  <div class="card">
                    <img src="${IMAGE_BASE + item.image.renditions['480x360'].link}" class="card-img-top" alt="...">
                    <div class="card-body">
                      <h5 class="card-title">${item.name}</h5>
                      <p class="card-text">${item.description}</p>
                    </div>
                </div>`));
        });
      })(jQuery);
    </script>
  </body>
</html>

Run the frontend

The frontend needs to be run on a web server. In your terminal go to /basic-headless-demos/frontend and start a web server, then access your page in the browser.

For example, use the http-server npm package:

npm install http-server -g
http-server
Just opening the index.html file in a web browser, for example by double-clicking on it, will result in a CORS errors.

Permissions for Images

What? No images displaying? We need to allow anonymous access to the image urls since we are running on the author instance.

  • Open the Security app, open the Roles tab, and edit the anonymous role.

  • Go to Web access tab, click Add new and enter the path /.imaging* set to GET.

README security anonymous imaging

Now refresh your client, et voila - images!

Congratulations

In the meantime we have the design. Our marketers want to enhance the travel portfolio page. They want to be able to add promotional teaser on top of the page and a custom sidebar too. They want to be able to define freely what to add there and add new article pages. They want to have full control of the navigation.

Feedback

Headless