Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Update in order to use REST 2.1 / delivery e.p.
HTML Wrap
floatright
classmenu

Related topics:

Resources:

This page tutorial explains how to access Magnolia content from a client-side application. We use Magnolia's built-in REST API and a single-page AngularJS application.

We use the classic cars from the My first content app tutorial as sample content. The cars are typical structured content: each car has the same content type and same fields. Structured content is easy to enter, query, analyze and publish to multiple channels. You can publish it to a website or consume it from a client-side application like we do in this tutorial. With the REST API, you the Magnolia Delivery endpoint API you can access any Magnolia Magnolia workspace.

Image Modified

If you want to know how to fetch content app data on the server side, for instance from a FreeMarker template script, see Accessing content on the server side.

Table of Contents
maxLevel3
minLevel2

Overview

Your tasks:

  • Install the Magnolia REST moduleThink about the REST securitya Magnolia bundle with a custom content app.
  • Configure the delivery endpoint in a light module.
  • Test REST access and think about REST security.
  • Write an Angular app which consumes JSON from the configured REST endpoint.

Architecture overview:

Image RemovedImage Added

Install

...

Make sure that your Magnolia installation contains the REST module. You should have at least the following submodules:

  • magnolia-rest-integration 
  • magnolia-rest-services

If you use a preconfigured Magnolia bundle or webapp you already have the required modules. If you use a custom bundle check your project dependencies and add Magnolia REST module if not already there.

If you want to use Swagger to test the REST endpoints, also install the magnolia-rest-tools module. It is not required to run the Angular app but it can be helpful during development.

(warning) When using magnolia-rest-tools, set the apiBasepath. The default value is most likely not correct for your Magnolia instance.

REST endpoints

The Magnolia REST module comes with preconfigured REST endpoints to read data from content apps. In this tutorial, we access content stored in the JCR so we use the following endpoints:

These two endpoints allow you to create, update and delete nodes and properties in any JCR workspace of your Magnolia instance.

Tip: You can also implement custom REST endpoints. You don't need them in this tutorial but custom endpoints are useful for:

  • Getting a very specific data structure which is different from what you get when using the preconfigured endpoints.
  • Exposing data from a custom content app which does not store the data in the JCR. 
  • Getting data from more than one workspace within one request.

REST security

Info
iconfalse

REST endpoints may be a security risk. Set permissions correctly. Read REST API security and define security for your specific requirements.

The preconfigured REST endpoints provide not only methods to read but create, edit and update data. Configure a specific role which meets your requirements. Add the role to the user group which should have access to the required REST methods. For instance, you could configure a role which can only read the nodes and properties of a specific workspace, then add this role to the anonymous user. During development it will help to add this new role to superuser too.

Install sample content

In this tutorial we use the Products app and the classic cars as sample content. The cars are stored in the products JCR workspace.

Image Removed

...

Artifact maven dependencies snippet
groupIdinfo.magnolia.documentation
artifactIdmagnolia-app-tutorial
label$artifactId.jar
renderTypedownload_link
resourceTypeJAR

...

  • Artifact resource link
    groupIdinfo.magnolia.documentation
    artifactIdmagnolia-app-tutorial
    label$artifactId.jar
    renderTypedownload_link
    resourceTypeJAR

If you don't have a Magnolia instance yet, follow Installing Magnolia and a content app, then come back here. You will end up with exactly what we want to have for this tutorial as well.

Grant permissions to access content via REST

Grant permission to read content from the workspace products to a new role.

  1. Open the Security app.
  2. Add a role read-products
    Image Removed 
  3. In Access control lists, grant the new role:
    • Read-only access to / and its subnodes in the Products workspace
    • Read-only access to /read-products in the Userroles workspace.
      Image Removed
  4. In Web access, grant Get access to the path /.rest/nodes/v1/products*. Deny all others.
    Image Removed 
  5. On the author instance, add the read-products role to the superuser system account.
  6. On the author instance, publish the  read-products role. 
  7. On the public instance, add the read-products role to the anonymous system account.
  8. Log out and log back in to apply the new permissions to the currently logged-in user. This gets you a new session.

Test the app

Open the Products app installed by the app-tutorial module and get familiar with it. 

Image Removed

Since the items in the app are cars let's talk about cars from now on. Try the tree, list and thumbnail views. Add a car of your own.

Image Removed   Image Removed

Test REST access

Request the app content with a REST call. Try the following request in your browser:

Code Block
languagexml
http://localhost:8080/magnoliaAuthor/.rest/nodes/v1/products/cars/007/Aston-Martin-DB5

Magnolia responds with:

...

titleClick here to expand to show the response from the REST endpoint

a Magnolia bundle containing the app-tutorial content app

For this tutorial you need a Magnolia bundle with:

  • Magnolia REST modules version 2.1 or higher: install Magnolia version 5.6.4 or higher to ensure it contains Magnolia REST modules version 2.1+.

    You may install a Community Edition (CE) or Enterprise Edition (EE) Magnolia bundle, with or without the demo for the purpose of this tutorial. We have used the magnolia-community-demo-webapp.

  • The latest version of the app-tutorial content app :  
    Artifact resource link
    groupIdinfo.magnolia.documentation
    artifactIdmagnolia-app-tutorial
    label$version
    renderTypedisplay_only
    resourceTypeJAR
    Artifact resource link
    groupIdinfo.magnolia.documentation
    artifactIdmagnolia-app-tutorial
    label$artifactId.jar
    renderTypedownload_link
    resourceTypeJAR

To install the bundle together with the app, we recommend using Magnolia CLI jumpstart

Once installed, the directory where you exectued the jumpstart command looks similar to this:

Code Block
erics-cars-tutorial/
├── apache-tomcat
└── light-modules

Remember the light-modules folder. You will add files there later on. 

Now start the bundle:

Code Block
cd erics-cars-tutorial/
mgnl start

Give Magnolia some time to finish the installation during the first start.

Then open a browser, open the URL http://localhost:8080/magnoliaAuthor and login with user superuser / password superuser.

About the custom content app

Click the Products tile open the app.

Image Added

This content app has two subapps:

  • The Browser subapp
    Image Added
  • The Detail subapp 
    Image Added

Content in the app-tutorial

You need the following information to properly configure REST access to the content:

  • The items of this app are cars. They are stored in the JCR workspace products.
  • The node type of a car is mgnl:product.
  • The items are organized in folders whose content type is mgnl:folder.
  • The images of the cars are assets stored in the dam workspace. Car nodes store a reference to a dam asset.
Tip

If you aim to fetch JCR content as JSON, analyze the JCR content using the JCR Browser app to understand all its details.

This is the products workspace displayed in the Tools>JCR browser app:

Image Added

Look at the image property. It stores an asset ID, a reference to an asset stored on the dam workspace.

Configure a delivery endpoint in a light module

Since Magnolia REST 2.1 you can define multiple delivery endpoints. You must configure at least one delivery endpoint to consume content as JSON from the delivery API. You can configure endpoints using both YAML and JCR.

To get the content from the app-tutorial as JSON, we will configure a delivery endpoint in a light module. In this example, we name the light module content-app-clients-v2.  

Within your light-modules folder, create this structure:

Code Block
content-app-clients-v2/
└── restEndpoints/
    └── delivery/
        └── carFinder.yaml

Note:

  • Endpoint definitions must reside within the folder restEndpoints. You can also use subfolders; here we created a subfolder called delivery.
  • The location of the endpoint definition within the folder restEndpoints partially defines the URL to access the configured endpoint. 
    Read Delivery endpoint API v2 - endpointPath property to understand the endpointPath and REST access URLs.
  • The above configuration leads to the following REST access URL: <magnolia-base-path>/.rest/delivery/carFinder

The definition file should look like this:

Code Pro
languageyaml
titlerestEndpoints/delivery/carFinder.yaml
linenumberstrue
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/raw/restEndpoints/delivery/carFinder.yaml?at=master

Note:

Test REST access and understand REST security

Before developping the Angular app, it is good practice to check if the API returns the JSON you expect and will be required by the Angular app. Check this using a browser.

REST URLs

We want to test the requests:

  • Access JSON for one car to display one car in detail.
  • Access JSON for all the cars and the folders they are wrapped with to display a list of cars to select from.

The requests should look like this:

  • <magnolia-base-path>/.rest/delivery/carFinder/

...

  • cars/007/Aston-Martin-DB5

...

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<node>
  <identifier>ce39509e-e1d5-4cdd-b254-125948e2aeec</identifier>
  <name>Aston-Martin-DB5</name>
  <path>/cars/007/Aston-Martin-DB5</path>
  <properties>
    <property>
      <multiple>false</multiple>
      <name>description</name>
      <type>String</type>
      <values>
        <value>&lt;p&gt;&lt;strong&gt;Movie:&lt;/strong&gt;&amp;nbsp;Goldfinger, GoldenEye, Casino Royale and Skyfall&lt;br
          /&gt;
          &lt;strong&gt;Year:&lt;/strong&gt;&amp;nbsp;1964, 1995, 2006 and 2012&lt;/p&gt;

          &lt;p&gt;The Aston Martin DB5 is one of the most famous cars in the world thanks to Oscar-winning special
          effects expert John Stears, who created the deadly silver-birch DB5 for use by James Bond in Goldfinger
          (1964). Although Ian Fleming had placed Bond in a DB Mark III in the novel, the DB5 was the company&amp;#39;s
          latest model when the film was being made.&lt;/p&gt;

          &lt;p&gt;A different Aston Martin DB5 (registration BMT 214A) was used in the 1995 Bond film, GoldenEye, in
          which three different DB5s were used for filming. The BMT 214A also returned in Tomorrow Never Dies (1997) and
          was set to make a cameo appearance in the Scotland-set scenes in The World Is Not Enough (1999), but these
          were cut in the final edit. Yet another DB5 appeared in Casino Royale (2006), this one with Bahamian number
          plates and left-hand drive (where the previous British versions had been right-hand drive).&lt;/p&gt;

          &lt;p&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/Aston_Martin_DB5"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
        </value>
      </values>
    </property>
    <property>
      <multiple>false</multiple>
      <name>title</name>
      <type>String</type>
      <values>
        <value>Aston Martin DB5</value>
      </values>
    </property>
    <property>
      <multiple>false</multiple>
      <name>image</name>
      <type>String</type>
      <values>
        <value>jcr:4cd02639-167d-405a-923f-607aa20d0bc0</value>
      </values>
    </property>
  </properties>
  <type>mgnl:product</type>
</node> 
  • (for one car)
  • <magnolia-base-path>/.rest/delivery/carFinder/cars (for all cars)

delivery/carFinder is the endpointPath defined by the endpoint configuration.

On magnoliaAuthor

You must be logged in to test the requests on the magnoliaAuthor webapp. Open a browser, request just http://localhost:8080/magnoliaAuthor/ and log in with superuser as the user name and password.

Now test the REST requests:

On magnoliaPublic

In a productive environment you request JSON from the public instance. In this tutorial we request the JSON from the magnoliaPublic webapp. Do not log in so that you test as the anonymous user. 

JSON responses

This is what the JSON responses look like:

Expand
titleClick here to see the JSON of a single car
Code Block
linenumberstrue
{
  "@name": "Aston-Martin-DB5",
  "@path": "/cars/007/Aston-Martin-DB5",
  "@id": "ce39509e-e1d5-4cdd-b254-125948e2aeec",
  "@nodeType": "mgnl:product",
  "description": "<p><strong>Movie:</strong>&nbsp;Goldfinger, GoldenEye, Casino Royale and Skyfall<br />\n<strong>Year:</strong>&nbsp;1964, 1995, 2006 and 2012</p>\n\n<p>The Aston Martin DB5 is one of the most famous cars in the world thanks to Oscar-winning special effects expert John Stears, who created the deadly silver-birch DB5 for use by James Bond in Goldfinger (1964). Although Ian Fleming had placed Bond in a DB Mark III in the novel, the DB5 was the company&#39;s latest model when the film was being made.</p>\n\n<p>A different Aston Martin DB5 (registration BMT 214A) was used in the 1995 Bond film, GoldenEye, in which three different DB5s were used for filming. The BMT 214A also returned in Tomorrow Never Dies (1997) and was set to make a cameo appearance in the Scotland-set scenes in The World Is Not Enough (1999), but these were cut in the final edit. Yet another DB5 appeared in Casino Royale (2006), this one with Bahamian number plates and left-hand drive (where the previous British versions had been right-hand drive).</p>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Aston_Martin_DB5\">Wikipedia</a></p>\n",
  "title": "Aston Martin DB5",
  "image": {
    "@name": "Aston-Martin-DB5.jpg",
    "@path": "/cars/007-cars/Aston-Martin-DB5.jpg",
    "@id": "jcr:4cd02639-167d-405a-923f-607aa20d0bc0",
    "@link": "/magnoliaPublic/dam/jcr:4cd02639-167d-405a-923f-607aa20d0bc0/Aston-Martin-DB5.jpg"
  },
  "@nodes": []
}

Lines 8-13: Note that the reference to the asset (which is stored in the dam workspace) has been resolved.

Expand
titleClick here to see the JSON of all the cars
Code Block
{
  "@name": "cars",
  "@path": "/cars",
  "@id": "6254df84-93b1-4431-b6dd-53b7b601c03d",
  "@nodeType": "mgnl:folder",
  "classics": {
    "@name": "classics",
    "@path": "/cars/classics",
    "@id": "45aa1b8d-0cd3-4829-9f17-02b4603910c1",
    "@nodeType": "mgnl:folder",
    "Continental-Mark-II": {
      "@name": "Continental-Mark-II",
      "@path": "/cars/classics/Continental-Mark-II",
      "@id": "dfeb5ad9-d06c-4e76-85e1-f30ef97c8bca",
      "@nodeType": "mgnl:product",
      "description": "<p>The Continental Mark II is a personal luxury car that was produced by Continental in 1956 and 1957. An attempt to build a post-World War II car to rival the greatest of the pre-War era, or anything produced in Europe, it is regarded as a rare and elegant classic.</p>\n\n<p>Ford wanted a superior and standalone up-market brand &ndash; aside from Lincoln &ndash; to compete with General Motors&#39; Cadillac, Packard, and Chrysler Corporation&#39;s Imperial brands.</p>\n\n<p>The new Continental was not intended to be the largest or most powerful automobile; rather, the most luxurious and elegant American car available, designed to recapture the spirit of the great classics of the prewar period&mdash;with prices to match. The Mark II&#39;s inspiration was the celebrated V12-powered Lincoln Continental of the 1940s, among the most notable cars of that War-interrupted decade.</p>\n",
      "title": "Continental Mark II",
      "image": {
        "@name": "7791746590_b5d3139a3f_b.jpg",
        "@path": "/cars/classic-cars/7791746590_b5d3139a3f_b.jpg",
        "@id": "jcr:5f29b11a-1c37-4f3e-a0e1-ef033b84998c",
        "@link": "/magnoliaPublic/dam/jcr:5f29b11a-1c37-4f3e-a0e1-ef033b84998c/7791746590_b5d3139a3f_b.jpg"
      },
      "@nodes": []
    },
    "1927-Hudson": {
      "@name": "1927-Hudson",
      "@path": "/cars/classics/1927-Hudson",
      "@id": "3e171627-43b4-47f8-bb82-dccbfac2c07f",
      "@nodeType": "mgnl:product",
      "description": "<p>The Hudson Motor Car Company made Hudson and other brand automobiles in Detroit, Michigan, from 1909 to 1954. In 1954, Hudson merged with Nash-Kelvinator Corporation to form American Motors (AMC). The Hudson name was continued through the 1957 model year, after which it was discontinued.</p>\n\n<p>The company had a number of firsts for the auto industry; these included dual brakes, the use of dashboard oil-pressure and generator warning lights, and the first balanced crankshaft, which allowed the Hudson straight-six engine, dubbed the &quot;Super Six&quot; (1916), to work at a higher rotational speed while remaining smooth, developing more power for its size than lower-speed engines. The dual brake system used a secondary mechanical emergency brake system, which activated the rear brakes when the pedal traveled beyond the normal reach of the primary system; a mechanical parking brake was also used. Hudson transmissions also used an oil bath and cork clutch mechanism that proved to be as durable as it was smooth.</p>\n",
      "title": "1927 Hudson",
      "image": {
        "@name": "5773574199_2c5cc1d891_b.jpg",
        "@path": "/cars/classic-cars/5773574199_2c5cc1d891_b.jpg",
        "@id": "jcr:8290ac0e-d9ab-4349-8ff9-100dc5d73eb8",
        "@link": "/magnoliaPublic/dam/jcr:8290ac0e-d9ab-4349-8ff9-100dc5d73eb8/5773574199_2c5cc1d891_b.jpg"
      },
      "@nodes": []
    },
    "Pontiac-Chieftain-1952": {
      "@name": "Pontiac-Chieftain-1952",
      "@path": "/cars/classics/Pontiac-Chieftain-1952",
      "@id": "bc84be8c-d9d5-4d2f-987b-e2cc5302aee0",
      "@nodeType": "mgnl:product",
      "description": "<p>The Pontiac Chieftain is an automobile that was produced by the Pontiac from 1949 to 1958. Chieftains were one of the first all new car designs to come to Pontiac in the post World War II years. Previous cars had been 1942 models with minor revisions.</p>\n\n<p>The Chieftain was initially introduced with four models: Sedan, Sedan Coupe, Business Coupe and Deluxe Convertible Coupe. In 1950, a Catalina Coupe was added to the range while a station wagon was added in 1952, with the demise of the top of the line Streamliner wagon.</p>\n\n<p>Some of the more interesting optional items available for the first generation Chieftain included a radio with seven vacuum tubes, tissue dispenser, under seat heaters, and a Remington Auto-Home shaver. In 1951, the horsepower on the 8-cylinder rose to 116. The Chieftain came with a gas gauge, ammeter, oil pressure gauge, and a temperature gauge which had marks for 160, 180, and 220 degrees Fahrenheit.</p>\n",
      "title": "Pontiac Chieftain 1952",
      "image": {
        "@name": "6218486709_743bd40d36_b.jpg",
        "@path": "/cars/classic-cars/6218486709_743bd40d36_b.jpg",
        "@id": "jcr:6df7ee03-5b11-46d4-a15f-579bcffb50b9",
        "@link": "/magnoliaPublic/dam/jcr:6df7ee03-5b11-46d4-a15f-579bcffb50b9/6218486709_743bd40d36_b.jpg"
      },
      "@nodes": []
    },
    "Fiat-Cinquecento": {
      "@name": "Fiat-Cinquecento",
      "@path": "/cars/classics/Fiat-Cinquecento",
      "@id": "ed450660-c375-443b-84bb-95a11846ccf0",
      "@nodeType": "mgnl:product",
      "description": "<p>The Fiat 500 (Italian: Cinquecento) was a city car produced by the Italian manufacturer Fiat between 1957 and 1975.</p>\n\n<p>Launched as the Nuova (new) 500 in July 1957, it was a cheap and practical town car. Measuring only 2.97 metres (9 feet 9 inches) long, and originally powered by an appropriately sized 479 cc two-cylinder, air-cooled engine, the 500 redefined the term &quot;small car&quot; and is considered one of the first city cars.</p>\n\n<p>Despite its diminutive size, the 500 proved to be an enormously practical and popular vehicle throughout Europe. Besides the two-door coup&eacute;, it was also available as the &quot;Giardiniera&quot; station wagon; this variant featured the standard engine laid on its side, the wheelbase lengthened by 10 cm (3.9 in) to provide a more convenient rear seat, a full-length sunroof, and larger brakes from the Fiat 600.</p>\n",
      "photoCredit": "werwer",
      "title": "Fiat Cinquecento",
      "image": {
        "@name": "6791962744_dc749ec56b_b.jpg",
        "@path": "/cars/classic-cars/6791962744_dc749ec56b_b.jpg",
        "@id": "jcr:1bbae8c4-26e3-4e2e-83b1-7658565e6e09",
        "@link": "/magnoliaPublic/dam/jcr:1bbae8c4-26e3-4e2e-83b1-7658565e6e09/6791962744_dc749ec56b_b.jpg"
      },
      "@nodes": []
    },
    "Riley-Brooklands-1930": {
      "@name": "Riley-Brooklands-1930",
      "@path": "/cars/classics/Riley-Brooklands-1930",
      "@id": "ba03c082-a68d-46c0-afe8-84d3fdcc6bd2",
      "@nodeType": "mgnl:product",
      "description": "<p>The Riley Nine was one of the most successful light cars produced by the British motor industry in the inter war period. It was made by the Riley company of Coventry, England with a wide range of body styles between 1926 and 1938.</p>\n\n<p>The car was largely designed by two of the Riley brothers, Percy and Stanley. Stanley was responsible for the chassis, suspension and body and the older Percy designed the engine.</p>\n\n<p>The 1,087 cc four-cylinder engine had hemispherical combustion chambers with the valves inclined at 45 degrees in a crossflow head. To save the expense and complication of overhead camshafts, the valves were operated by two camshafts mounted high in the crankcase through short pushrods and rockers. The engine was mounted in the chassis by a rubber bushed bar that ran through the block with a further mount at the rear of the gearbox. Drive was to the rear wheels through a torque tube and spiral bevel live rear axle mounted on semi elliptic springs.</p>\n",
      "title": "Riley Brooklands 1930",
      "image": {
        "@name": "5158386021_1fffd2fb4d_b.jpg",
        "@path": "/cars/classic-cars/5158386021_1fffd2fb4d_b.jpg",
        "@id": "jcr:57cc5aa2-4d72-4b05-be2a-b02856e9f91c",
        "@link": "/magnoliaPublic/dam/jcr:57cc5aa2-4d72-4b05-be2a-b02856e9f91c/5158386021_1fffd2fb4d_b.jpg"
      },
      "@nodes": []
    },
    "@nodes": [
      "Continental-Mark-II",
      "1927-Hudson",
      "Pontiac-Chieftain-1952",
      "Fiat-Cinquecento",
      "Riley-Brooklands-1930"
    ]
  },
  "007": {
    "@name": "007",
    "@path": "/cars/007",
    "@id": "8ae34efb-52bd-4b94-a484-2677be490c9b",
    "@nodeType": "mgnl:folder",
    "Aston-Martin-DB5": {
      "@name": "Aston-Martin-DB5",
      "@path": "/cars/007/Aston-Martin-DB5",
      "@id": "ce39509e-e1d5-4cdd-b254-125948e2aeec",
      "@nodeType": "mgnl:product",
      "description": "<p><strong>Movie:</strong>&nbsp;Goldfinger, GoldenEye, Casino Royale and Skyfall<br />\n<strong>Year:</strong>&nbsp;1964, 1995, 2006 and 2012</p>\n\n<p>The Aston Martin DB5 is one of the most famous cars in the world thanks to Oscar-winning special effects expert John Stears, who created the deadly silver-birch DB5 for use by James Bond in Goldfinger (1964). Although Ian Fleming had placed Bond in a DB Mark III in the novel, the DB5 was the company&#39;s latest model when the film was being made.</p>\n\n<p>A different Aston Martin DB5 (registration BMT 214A) was used in the 1995 Bond film, GoldenEye, in which three different DB5s were used for filming. The BMT 214A also returned in Tomorrow Never Dies (1997) and was set to make a cameo appearance in the Scotland-set scenes in The World Is Not Enough (1999), but these were cut in the final edit. Yet another DB5 appeared in Casino Royale (2006), this one with Bahamian number plates and left-hand drive (where the previous British versions had been right-hand drive).</p>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Aston_Martin_DB5\">Wikipedia</a></p>\n",
      "title": "Aston Martin DB5",
      "image": {
        "@name": "Aston-Martin-DB5.jpg",
        "@path": "/cars/007-cars/Aston-Martin-DB5.jpg",
        "@id": "jcr:4cd02639-167d-405a-923f-607aa20d0bc0",
        "@link": "/magnoliaPublic/dam/jcr:4cd02639-167d-405a-923f-607aa20d0bc0/Aston-Martin-DB5.jpg"
      },
      "@nodes": []
    },
    "Sunbeam-Alpine": {
      "@name": "Sunbeam-Alpine",
      "@path": "/cars/007/Sunbeam-Alpine",
      "@id": "7ff3597f-bd68-4293-969f-9a9d13619ea6",
      "@nodeType": "mgnl:product",
      "description": "<p><strong>Movie:</strong>&nbsp;Dr. No<br />\n<strong>Year:&nbsp;</strong>1962</p>\n\n<p>The Sunbeam Alpine is a sporty two-seat open car produced by Sunbeam from 1953 to 1955, and then 1959 to 1968. The name was then used on a two-door fastback from 1969 to 1975. The original Alpine was launched in 1953 as the first vehicle from Sunbeam-Talbot to bear the Sunbeam name alone since the 1935 takeover of Sunbeam and Talbot by the Rootes Group.</p>\n\n<ul>\n\t<li>A metallic blue 1953 Sunbeam Alpine Mk.1 is driven by Grace Kelly in To Catch a Thief (1955) with Cary Grant.</li>\n\t<li>A lake blue &quot;Series II&quot; Alpine roadster is amongst the first on-screen &quot;Bond cars&quot; when it is rented and driven by James Bond in Dr. No of 1962. It was reportedly borrowed from a local resident, as the only suitable sports car available on the island used for filming.</li>\n\t<li>A Sunbeam Alpine is driven by Michael Caine in the 1967 film Gambit. The film also starred Shirley MacLaine and Herbert Lom.</li>\n</ul>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Sunbeam_Alpine\">Wikipedia</a></p>\n\n<p>&nbsp;</p>\n",
      "title": "Sunbeam Alpine",
      "image": {
        "@name": "Sunbeam-Alpine.jpg",
        "@path": "/cars/007-cars/Sunbeam-Alpine.jpg",
        "@id": "jcr:061b70de-07fb-4fa0-be60-ab82e4b43c70",
        "@link": "/magnoliaPublic/dam/jcr:061b70de-07fb-4fa0-be60-ab82e4b43c70/Sunbeam%20Alpine.jpg"
      },
      "@nodes": []
    },
    "Aston-Martin-V8": {
      "@name": "Aston-Martin-V8",
      "@path": "/cars/007/Aston-Martin-V8",
      "@id": "40938f8b-37d6-4bc8-80eb-1e06d3c46557",
      "@nodeType": "mgnl:product",
      "description": "<p><strong>Movie:</strong>&nbsp;The Living Daylights<br />\n<strong>Year:</strong>&nbsp;1987</p>\n\n<p>At the beginning of the film, the car is a V8 Volante (convertible). Later, the car is fitted with a hardtop (&quot;winterised&quot;) at Q Branch.</p>\n\n<p>The alterations and gadgets featured were:</p>\n\n<ul>\n\t<li>Police band radio</li>\n\t<li>Tire Spikes</li>\n\t<li>Jet engine behind rear number plate</li>\n\t<li>Retractable outriggers</li>\n\t<li>Heat-seeking missiles behind fog lights</li>\n\t<li>Lasers in front wheel hubcaps</li>\n\t<li>Bulletproof windows</li>\n\t<li>Fireproof body</li>\n\t<li>Self-destruct system</li>\n</ul>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Aston_Martin_V8_Vantage_%281977%29\">Wikipedia</a></p>\n",
      "title": "Aston Martin V8",
      "image": {
        "@name": "Aston-Martin-V8.jpg",
        "@path": "/cars/007-cars/Aston-Martin-V8.jpg",
        "@id": "jcr:129b1675-c383-46ae-97a8-b1ba18c0ab9d",
        "@link": "/magnoliaPublic/dam/jcr:129b1675-c383-46ae-97a8-b1ba18c0ab9d/Aston%20Martin%20V8.jpg"
      },
      "@nodes": []
    },
    "Lotus-Esprit-S1": {
      "@name": "Lotus-Esprit-S1",
      "@path": "/cars/007/Lotus-Esprit-S1",
      "@id": "c9342333-4b85-4ebb-b978-478742ecee96",
      "@nodeType": "mgnl:product",
      "description": "<p><strong>Movie:</strong>&nbsp;The Spy Who Loved Me<br />\n<strong>Year:</strong>&nbsp;1977</p>\n\n<p>Delivered to Bond by Q in Sardinia, this Lotus is capable of transforming into a submarine. In this mode, it is equipped with anti-aircraft missiles.&nbsp;</p>\n\n<p>Source <a href=\"https://en.wikipedia.org/wiki/List_of_James_Bond_vehicles\">Wikipedia</a></p>\n\n<p>The Lotus Esprit is a sports car that was built by Lotus in the United Kingdom between 1976 and 2004. The silver Italdesign concept that eventually became the Esprit was among the first of designer Giorgetto Giugiaro&#39;s polygonal &quot;folded paper&quot; designs.&nbsp;</p>\n\n<p>The Esprit was powered by the Lotus 907 4-cylinder engine, as previously used in the Jensen Healey. This engine displaced 2.0 L, produced 160 bhp (119 kW; 162 PS) in European trim 140 bhp (104 kW; 142 PS) in US/Federal trim, and was mounted longitudinally behind the passengers, as in its predecessor.&nbsp;</p>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Lotus_Esprit#S1_.281976.29\">Wikipedia</a></p>\n",
      "title": "Lotus Esprit S1",
      "image": {
        "@name": "Lotus-Esprit-S1.jpg",
        "@path": "/cars/007-cars/Lotus-Esprit-S1.jpg",
        "@id": "jcr:e675a1cf-2569-40ab-b105-4f9870a7493a",
        "@link": "/magnoliaPublic/dam/jcr:e675a1cf-2569-40ab-b105-4f9870a7493a/Lotus-Esprit-S1.jpg"
      },
      "@nodes": []
    },
    "@nodes": [
      "Aston-Martin-DB5",
      "Sunbeam-Alpine",
      "Aston-Martin-V8",
      "Lotus-Esprit-S1"
    ]
  },
  "@nodes": [
    "classics",
    "007"
  ]
}

Anchor
acl-REST-security
acl-REST-security
REST security

REST APIs are a powerful feature but they may also turn into a security risk. This is why it is important you understand how Magnolia handles security for REST

Note

We strongly recommend you read and understand the page REST security.

For this tutorial, note the following points.

There are two levels of control when REST requests are issued on the delivery API:

  • Web access (checking URLs) 
  • JCR access (checking access to JCR workspaces)

Both web and JCR access must be granted. In Magnolia it is granted using Roles with access control lists that are assigned to users and groups. 

When you tested the requests above, you used:

  • superuser on magnoliaAuthor
  • anonymous user on magnoliaPublic

These users have very different security settings. While superuser typically has a lot of permissions, anonymous has only limited access for both web access and JCR security. Requests made using the anonymous user on magnoliaPublic only worked because:

  • Web access is granted by the role rest-anonymous that comes by default with the REST modules and is assigned to anonymous user.
  • JCR access was bypassed with the delivery endpoint configuration by setting the property bypassWorkspaceAcls.
Note

In a production environment:

  • Do not use the property bypassWorkspaceAcls in endpoint definitions.
  • Create custom REST roles granting specific access for specific use cases.

Create an AngularJS app

AngularJS is a Web application framework popular among front-end developers. In this tutorial,

We got an XML response but actually we want JSON. The nodes endpoint requires an HTTP request header to return JSON. Request the content with cURL so you can pass the desired return type.

Open a terminal and type the following command:

Code Block
languagebash
curl -H "Accept: application/json"  http://localhost:8080/magnoliaAuthor/.rest/nodes/v1/products/cars/007/Aston-Martin-DB5 -u superuser:superuser

Now we get JSON:

Code Block
languagejs
{
    "name": "Aston-Martin-DB5",
    "type": "mgnl:product",
    "path": "/cars/007/Aston-Martin-DB5",
    "identifier": "ce39509e-e1d5-4cdd-b254-125948e2aeec",
    "properties": [{
        "name": "description",
        "type": "String",
        "multiple": false,
        "values": ["<p><strong>Movie:</strong>&nbsp;Goldfinger, GoldenEye, Casino Royale and Skyfall<br />\n<strong>Year:</strong>&nbsp;1964, 1995, 2006 and 2012</p>\n\n<p>The Aston Martin DB5 is one of the most famous cars in the world thanks to Oscar-winning special effects expert John Stears, who created the deadly silver-birch DB5 for use by James Bond in Goldfinger (1964). Although Ian Fleming had placed Bond in a DB Mark III in the novel, the DB5 was the company&#39;s latest model when the film was being made.</p>\n\n<p>A different Aston Martin DB5 (registration BMT 214A) was used in the 1995 Bond film, GoldenEye, in which three different DB5s were used for filming. The BMT 214A also returned in Tomorrow Never Dies (1997) and was set to make a cameo appearance in the Scotland-set scenes in The World Is Not Enough (1999), but these were cut in the final edit. Yet another DB5 appeared in Casino Royale (2006), this one with Bahamian number plates and left-hand drive (where the previous British versions had been right-hand drive).</p>\n\n<p>Source: <a href=\"https://en.wikipedia.org/wiki/Aston_Martin_DB5\">Wikipedia</a></p>\n"]
    }, {"name": "title", "type": "String", "multiple": false, "values": ["Aston Martin DB5"]}, {
        "name": "image",
        "type": "String",
        "multiple": false,
        "values": ["jcr:4cd02639-167d-405a-923f-607aa20d0bc0"]
    }]
}

If you installed the magnolia-rest-tools module, request the same with Swagger:

  1. Open the REST Tools app.
    rest-tools-launch-tile
  2. Click GET in the nodes API endpoint.
    rest-tools-swagger-nodes-api-overview
  3. Set workspace to products and path to a car such as /cars/007/Aston-Martin-DB5 and click Try it out. You get JSON data for one content item. 
    rest-tools-swagger-nodes-params   rest-tools-swagger-response

Play around a little bit to familiarize yourself with the Swagger tool. Figure out the correct parameters to get a JSON representation of all cars.

Create an AngularJS app

AngularJS is a popular Web application framework among front-end developers. In this tutorial we use an AngularJS app to access and render content from the Products app. We assume you know enough JavaScript to follow along.

...

Code Block
languagebash
cars/
├── 007/
│   ├── Aston-Martin-DB5
│   ├── Aston-Martin-V8
│   ├── Lotus-Esprit-S1
│   └── Sunbeam-Alpine
└── classics/
    ├── 1927-Hudson
    ├── Continental-Mark-II
    ├── Fiat-Cinquecento
    ├── Pontiac-Chieftain-1952
    └── Riley-Brooklands-1930

whereWhere:

  • cars, 007 and classics are folders.
  • the The cars are of type mgnl:product.

We will create Let's create an Angular app that provides:

  • List A list of cars to choose from.
  • Details of a selected car with a description and image.
  • Checkboxes to filter the list into classics or 007 cars. We use the parent folders as pseudo-categories.

App structure

We will create an ngApp  with two controllers:

  • carsList renders a list of all cars. Each car has a clickable label. Clicking on the label shows the car in the carDetail controller. The list controller also has two checkboxes to filter the list - ; one for each parent folder.

  • carDetail renders renders the detail details of the selected car: title, description and image.

Image Modified

App files

Create the following file Now create a folder called angular with the files app.jserics-cars.html and style.css in the light module, to create this structure:

Code Block
titlelightmodules/
$lightmodules
└── content-app-clients-v2/
    └──├── angular/
   ├──  app.js
│   ├── apperics-cars.jshtml
  └── style.css
└── restEndpoints
    ├── erics-cars.html└── delivery
        └── style.css 

 $lightmodules is your light module directory. It can be anywhere on your file system but it must be a real directory such as:

  • Mac OS X /Users/johndoe/dev/lightmodules
  • Windows C:\Users\johndoe\dev\lightmodules

You may want to set the magnolia.resources.dir property to reference your light modules folder:

Code Block
magnolia.resources.dir=/Users/johndoe/dev/lightmodules
carFinder.yaml
Info

To avoid running into

Tip

To avoid getting in trouble with the same-origin policy, we run the Angular app on the same server as Magnolia. See how to overcome same-origin policy problem. Instead of a Freemarker template, keep it simple and just add a three static files to a light module

In a real use case you would serve the Angular app not from Magnolia, but rather from a static webserver like Apache and most probably on a different (sub)domain.

CarsContentClient app

Start creating the Angular app in erics-cars.html. Add an ng-app directive to the body element which will wrap wraps the two controllers:

Code Block
languagexml
titlelightmodules/content-app-clients-v2/angular/erics-cars.html (intermediate state)
linenumberstrue
<!doctype html>
<html>
<head>
    <title>Angular loves Magnolia :-)</title>
    <link rel="stylesheet" href="style.css?u=12">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.0/angular.min.js"></script>
    <script src="app.js?v=w"></script>
</head>

<body ng-app="CarsContentClient">

<h1>Eric's cars - shown with a lightweight angular client</h1>

</body>
</html>

...

Request the file in the browser to make sure that Magnolia serves a static file. Use a dummy parameter ?foo=bar also in the request URL. It ensures we bypass cache on both server and client side.

Code Block
http://localhost:8080/magnoliaAuthor/.resources/content-app-clients/angular/erics-cars.html?foo=bar

server and client side.

https://git.magnolia-cms.com/projects/DOCUMENTATION/repos/
content-app-clients
/browse
-v2/angular/erics-cars.html?
at=master&raw 
foo=bar
Code Block
http://localhost:8080/magnoliaPublic/.resources/
Hide block
Code Pro
languagexml
titlecontent-app-clients/angular/erics-cars.html
linenumberstrue
sections%%(doctype)%% - %%(</h1>)%% , %%(</body>)%% - %%(</html>)%%
url

Next, configure the Angular app in app.js:

Code Pro
languagejs
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
sections%%(var app =)%% - %%(}\);)%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/app.js?at=master&raw
 

Now you have created the app and defined some constants to use later on.

CSS style sheet

To style the Eric's car page, add some CSS code to style.css

Code Pro
collapseTypeClassic
languagecss
titlelightmodules/content-app-clients-v2/angular/style.css
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/style.css?at=master&raw
 

...

Code Pro
languagexml
titlelightmodules/content-app-clients-v2/angular/erics-cars.html (fragment)
sections%%(ng-controller="carDetail")%% - %%(eof 'carDetail')%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/erics-cars.html?at=master&raw
 
Info

When you reload the file in the browser you may notice JavaScript errors. To see the errors, open the JavaScript console of your browser. The name of this " console " and how it is named depends on the browser you are using. For instance, on Google Chrome browser it is named DevTools.

globalData component

Add a component named globalData. Both controllers will use it. It has a property productPath which will be that is set by one controller and read by the other controller.

Code Pro
languagejs
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
sections%%(app.service\('globalData')%% - %%(}]\);)%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/app.js?at=master&raw
 

...

Add a component named utils. It provides some useful methods that both controllers can use.  The carDetail controller will We use the function #getPropertyValuefunction replaceAll in the carsList controller.

Code Pro
languagejs
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
sections%%(app.service\('utils')%% - %%(}\);)%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients/browse/angular/app.js?at=master&raw
 

Note:

...

-v2/browse/angular/app.js?at=master&raw
 

...

sanitize filter

The third little helper to add is the sanitize filter. Remember that the description property of the content item contains HTML, encoded HTML even. By default, Angular refuses to render JSON content that contains (HTML) markup. The sanitize filter makes sure that the (encoded) HTML is rendered properly as HTML. 

Code Pro
languagejs
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
sections%%(app.filter\("sanitize")%% - %%(}]\);)%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/app.js?at=master&raw
 

Internally the filter is using the angular Angular module  $sce .

carDetail controller

...

Code Pro
languagejs
firstline1
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
linenumberstrue
sections%%(app.controller\('carDetail')%% - %%(}]\);)%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients-v2/browse/angular/app.js?at=master&raw
 

...

  • Line 1: Custom components APP_CONFIGglobalData and utils are injected.
  • Line 9: The controller executes an HTTP#get method on the nodes REST endpoint. The content item path is the selected item. It requests data for one content item as we did in testing REST access.
  • Line 8, 9: Computes a request URL for the REST call using configuration data from the globalData component.
  • Line 3ff: The HTTP#get call is defined as a callback function of $watch which is a value change listener for globalData.productPath. The REST call is executed initially and when globalData.productPath changes.

Reload the file in your browser again. Now you can see the detail of the Aston Martin DB5 in your browser. Cool! Isn't it?

Image Removed

carsList controller

The carsList controller renders a list of clickable span elements that contain car titles. The list can be filtered with checkboxes using the parent folders classics and 007 as pseudo-categories.

Add this HTML in erics-cars.html:

Code Pro
languagexml
titlelightmodules/content-app-clients/angular/erics-cars.html (fragment)
sections%%(ng-controller="carsList")%% - %%(eof 'carsList')%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients/browse/angular/erics-cars.html?at=master&raw
 

The $scope of this controller has the following properties:

  • cars: An array of car items. Each car item has these properties:
    • path
    • category: actually a pseudo-category, the name of the parent folder such as classics or 007.
    • title: Title of the car
    • name: Node name of the item.
  • carCategories: An associative array (map) for the pseudo-categories. Each item has one property:
    • checked: Whether the pseudo-category is currently selected.
  • APP_CONFIGglobalData and utils are injected.
  • Lines 6, 7: Computes a request URL for the REST call using configuration data from the globalData component.
  • Line 7: The controller executes an HTTP#get method on the delivery REST endpoint on the prefix carFinder. The content item path is the selected item. It requests data for one content item as we did in testing REST access.
  • Line 2 and onward: The HTTP#get call is defined as a callback function of $watch which is a value change listener for globalData.productPath. The REST call is executed initially and when globalData.productPath  changes.

Reload the file in your browser. Now you can see the detail of the Aston Martin DB5 in your browser. Cool, isn't it!?

Image Added

carsList controller

The carsList controller renders a list of clickable span elements that contain car titles. The list can be filtered with checkboxes using the parent folders classics and 007 as pseudo-categories.

Add this HTML in erics-cars.htmlHere is the JavaScript code:

Code Pro
languagejsxml
firstline1
titlelightmodules/content-app-clientsclients-v2/angular/apperics-cars.js html (fragment)linenumberstrue
sections%%(app.controller\('carsList'ng-controller="carsList")%% - %%(}]\);eof 'carsList')%%
urlhttps://git.magnolia-cms.com/projects/DOCUMENTATION/repos/content-app-clients/browse/angular/app.js?at=master&raw
 

Note:

  • Line 1: Again custom components APP_CONFIGglobalData and utils are injected.
  • Lines 5-7: selectCar is executed when you click a car of the list. The function sets globalData.productPath so it indirectly triggers the execution of the carDetail controller.
  • Lines 9-11: clickCategory maintains the $scope variable carCategories. Since this changes the state of a controller $scope variable, the UI gets repainted - the list items gets updated.
  • Line 3: The controller executes an HTTP#get method. Here the URI for the JSON call requests the cars root folder and contains the argument depth.

Final result:

Image Removed

Get the completed files

The three files in their final form are available in Magnolia's Git repository.

Option 1: Clone the Git repository

Clone the repository to get the complete file structure. Use a terminal, go to your light modules folder, and clone:

-v2/browse/angular/erics-cars.html?at=master&raw
 

The $scope of this controller has the following properties:

  • cars: An array of car items.
    Each car item has these properties:
    • path
    • category: actually a pseudo-category, the name of the parent folder such as classics or 007.
    • title: Title of the car
    • name: Node name of the item.
  • carCategories: An associative array (map) for the pseudo-categories.
    Each item has one property:
    • checked: Whether the pseudo-category is currently selected.

Here is the JavaScript code:

git clone scmdocumentation.git
Code Pro
Code Block
languagebash
languagejs
firstline1
titlelightmodules/content-app-clients-v2/angular/app.js (fragment)
linenumberstrue
sections%%(app.controller\('carsList')%% - %%(}]\);)%%
url
https://git.magnolia-cms.com/projects/
DOCUMENTATION/
repos/content-app-clients

Option 2: Download

What next?

Custom JSON format

The JSON format provided by the nodes endpoint is not very handy. In this tutorial we managed to get the required properties with an extra JavaScript function in the utils component of the Angular app. But at some point you may want a JSON representation that you cannot get easily with the default JCR nodes and properties endpoints.

With a custom JSON service you can also get data from different JCR workspaces in one request. This cannot be done with the default endpoints for nodes and properties. A custom service therefore saves resources on the network.

With Java: create a custom endpoint 

Expose a custom endpoint. This approach requires some Java classes which must be packaged into a Magnolia Maven module.

Without Java: neat-jsonfn

Another solution is the neat-jsonfn module. Read the blog post and check out the module in GitHub:

Combine your content app with the Categorization module

...

-v2/browse/angular/app.js?at=master&raw
 

Note:

  • Line 1: Again custom components APP_CONFIGglobalData and utils are injected.
  • Lines 5-10: selectCar is executed when you click a car in the list. The function sets globalData.productPath so it indirectly triggers the execution of the carDetail controller.
    We remove the slash at the very begining, since we want a path relative to the rootPath as defined in the configuration for the carFinder prefix of the delivery endpoint.
  • Lines 12-14: clickCategory maintains the $scope variable carCategories. Since this changes the state of a controller $scope variable, the UI gets repainted - the list items get updated.
  • Line 21, 22: The controller executes an HTTP#get method. Here the URI for the JSON call requests the cars root folder and contains the argument depth.

Final result:

Image Added

The angular app is now fully functional. Congratulations! 

Get the completed files

The files in their final form are available in the Magnolia Git repository.

Clone the repository to get the complete file structure. Use a terminal, go to your light modules folder and clone:

Code Block
languagebash
git clone https://git.magnolia-cms.com/scm/documentation/content-app-clients-v2.git

What next?

Where to add the AngularJS app

...

You can use or host the AngularJS app anywhere:

  • As a static web resource served by Magnolia as seen in this tutorial.
  • As part of a page or component template, in a template script.
  • As a static or dynamic resource in any other server.

Anchor
anc-same-origin-policy
anc-same-origin-policy
Same-origin policy

If you want to run the AngularJS app on a distinct different host – a host which has a different address than the Magnolia server which provides the REST API – you run into the same-origin policy problem. To overcome this problem, some use specific Apache settings, see StackOverflow.

An elegant solution to solve the same-origin policy without changing Apache configuration is the Magnolia CORSFilter:

...

the Magnolia Add HTTP Headers filter.


...

Photo credits: