Your first Enonic Website

Contents

Your first Enonic Website

A step-by-step tutorial for building your first Website on the Enonic platform

Ready…​Set…​Code!

In order to complete this tutorial, the Enonic development environment must be installed on your computer: https://developer.enonic.com/enonic-101/install-developer-environment

Part 1: Project set up

In this first section, you will learn how to initialize a new project with the CLI toolbox that comes with Enonic XP. Then you will build and deploy the app with Gradle. Next you will create a couple of files for your first page component. Finally, you’ll create a site with your app in the Content Studio.

Initialize project

Enonic XP includes the Toolbox CLI which can perform several useful operations. The init-project operation will clone an existing project from a repository source, such as GitHub. The starter-vanilla project will initialize a new application with the standard structures required (see Projects).

  1. Create a new folder at a suitable location on your filesystem for the application project files. e.g. /Users/<username>/projects/myapp. This will be the project root.

  2. Change directory in the terminal to this project root.

    cd /Users/<username>/projects/myapp
  3. Run the following command, replacing [$XP_INSTALL] with the path to your unzipped XP installation:

    [$XP_INSTALL]/toolbox/toolbox.sh init-project -n com.company.myapp -r starter-vanilla -c v1.3.0

For example, if your XP installation is at /Users/enonic/installs/enonic-xp-${release} then you would enter:

/Users/enonic/installs/enonic-xp-${release}/toolbox/toolbox.sh init-project -n com.company.myapp -r starter-vanilla -c v1.3.0
Only basic characters (a-z, 0-9 and .) may be used for application names, and the name must be globally unique. We recommend following standard Java package naming conventions such as com.mycompany.myapp.

Your project folder will now be filled with the standard folder structure for developing an app.

Build and Deploy

Now that the basic project structure is set up, we should test that it builds and deploys successfully. But before deploying the app, the $XP_HOME environment variable must be set to the path of the home folder of the XP installation.

  1. Run the following command in the terminal, replacing [$XP_INSTALL] with your installation location (no brackets):

    Linux and OSX:

    export XP_HOME=[$XP_INSTALL]/home

    For example, if your XP installation is at /Users/enonic/installs/enonic-xp-${release} then you would enter:

    export XP_HOME=/Users/enonic/installs/enonic-xp-${release}/home

    Windows:

    set XP_HOME=[$XP_INSTALL]/home
  2. Execute the following command (from the project root directory):

    Linux and OSX:

    ./gradlew deploy

    Windows:

    gradlew deploy

    The included Gradle wrapper will build the app and then attempt to deploy it to your installation.

    The deployment step simply moves the result of the build (the application JAR file) into the $XP_HOME/deploy directory. From there, Enonic XP will detect, install and start the application automatically.

    You will need to access the Administrative console to check that the app has installed and started. Enonic XP must be running to proceed.

  3. Log in to the Administrative console with the Administrative user credentials. (The default credentials are userid: su and password: password).

  4. Navigate to the Applications Tool. The application you just deployed should be listed here.

  5. Click the app called "Myapp" to see information about it and confirm that it has started.

My First app deployed
You can change the display name of the application by editing the gradle.properties file.

Create the "Hello World" Site

Our next goal is to set up a "Hello World" site in Content Studio, but first we must add some initial configuration to our project.

Site descriptor

An application can serve many purposes and building sites is just one of them. The site.xml file is the descriptor that will let Enonic XP know that this app can be added to a site. Response filters and controller mappings can be set up in the site descriptor as well as application configurations (see Site descriptors).

A basic site.xml file was automatically created by the init-project script and we don’t need to make any changes for now. Later in this tutorial we will edit site.xml to add a site-wide configuration.

[project-root]/src/main/resources/site/site.xml

Application description and icon

The application.xml file at [project-root]/src/main/resources/application.xml can be edited to provide a suitable description for your app. Go ahead and give your app a custom description.

The application.svg file can be replaced with a custom app icon. The description and icon will be visible in the admin tools.

Most of the files we will be working with are inside the "site" directory in the project folder - src/main/resources/site. All file paths from now on will begin with `site/.

Page Component

Page components are the most basic building blocks of websites in Enonic XP (see Page). They require a JavaScript controller and optionally an XML descriptor and an HTML view. This first example does not need a descriptor file.

A page controller (see Page) is a JavaScript file that handles requests such as GET and POST. Controllers usually pass data in the form of a JavaScript object to be dynamically rendered in an HTML view. No data is passed in the example below, but the view file is specified and rendered as static HTML.

  1. Create a folder called hello inside the site/pages directory.

  2. Create the page controller and page view files specified below inside the hello folder:

    var thymeleaf = require('/lib/xp/thymeleaf'); // Import the thymeleaf library
    
    // Handle the GET request
    exports.get = function(req) {
    
        // Specify the view file to use
        var view = resolve('hello.html');
    
        // Render HTML from the view file
        var body = thymeleaf.render(view, {});
    
        // Return the response object
        return {
            body: body
        }
    };

    The view below is a simple HTML file. This file will be updated later to handle dynamic content.

    <!DOCTYPE html>
    <html>
        <head>
            <title>Hello world</title>
        </head>
        <body data-portal-component-type="page">
            <h1>Hello world</h1>
        </body>
    </html>
  3. Once these files are in place, redeploy the app from the terminal with ./gradlew deploy.

Each page controller must reside in its own folder under the site/pages directory. The name of the controller JavaScript file must be the same as the directory that contains it. The HTML view file can reside anywhere in the project and have any valid file name. This allows view files to be shared between components.

Create Site

Now that the files are in place, we can create the site in a browser using the Content Studio admin tool. Switch between different tools by clicking the Menu icon menu icon (top right) to open the Launcher panel.

  1. In your browser, navigate to the Content Studio tool. (Use the menu icon at the top right)

  2. Click New and select Site from the list of content types (Opens a tab for editing the new site).

  3. Fill in the form with Display Name: Hello World.

  4. Select your MyApp application in the Applications dropdown.

  5. If you don’t see a blue area on the right of the page then click Menu icon button in the toolbar to open the Page Editor.

  6. Use the dropdown in the Page Editor (blue area) to select the "hello" page.

  7. Click the Save draft button in the toolbar (top-left).

  8. Now close the Hello World site editor tab to see the content pane.

When you click on the Hello World site content, the preview should look something like this:

Hello World site

This concludes part one of the tutorial.

Let’s review:

You’ve learned how to initialize a new project with the CLI toolbox init-project.

Then you set the $XP_HOME environment variable and deployed the app with gradle.

Next, you created a page component with a JavaScript controller and an HTML view file.

Finally, you created a site in the Content Studio and added the app and page component to the site.

This might seem like a lot of work just to make a static page; but we have laid a solid foundation in preparation for dynamically displaying structured content in reusable components which you will learn about in the next section.

Part 2: Content types and parts

Now it is time to introduce content types for structured data and how to display the data with part components. You will also learn how to build pages and page templates with components and regions.

Add some Countries

In order to make our World slightly more interesting, we will add some countries as structured data.

The structure of data (such as countries) are defined in XML files and are referred to as Content Types. The content type defines the form (and underlying schema) of items you manage.

  1. Create a folder named “country” inside the “content-types” folder of your project.

  2. Create an XML file named “country.xml” in the “country” folder and paste in the code below for the Country content type.

    Country content type:
    <content-type>
      <display-name>Country</display-name>
      <super-type>base:structured</super-type>
      <form>
        <input type="TextArea" name="description">
          <label>Description</label>
          <occurrences minimum="0" maximum="1"/>
        </input>
        <input type="TextLine" name="population">
          <label>Population</label>
          <occurrences minimum="0" maximum="1"/>
        </input>
      </form>
    </content-type>

    Each content type can have a custom icon that will be visible in the Content Studio interface. Though not required, content icons can be helpful for content editors.

  3. Copy the image below to the the same folder (content-types/country) with the name country.png.

Country content type

This content type defines form inputs for description and population. Every content has a built-in field for Display Name. When the app is redeployed, you can create a new content of type Country in the Content Studio. The Country content type will produce the form seen below. Each country content that is created with this form will become a page in the site when a page component is added to it with the dropdown in the page editor on the right. More on this later.

Country Content form
Each content type must reside in its own folder under the site/content-types directory. The name of the content type XML file and the icon PNG file must be the same as the directory that contains them.

Create the Country Part

We also need a way to display the data from our Country content type. This time, rather than making another page controller, we will create a Part component. Parts are reusable components that can be added to regions in pages or layout components - more on this later.

  1. Create a folder called country inside the parts folder in your project.

  2. Add the part controller and view files inside the country folder:

    Country part controller:
    var portal = require('/lib/xp/portal'); // Import the portal library
    var thymeleaf = require('/lib/xp/thymeleaf'); // Import the Thymeleaf library
    
    // Handle the GET request
    exports.get = function(req) {
    
        // Get the country content as a JSON object
        var content = portal.getContent();
    
        // Prepare the model object with the needed data from the content
        var model = {
            name: content.displayName,
            description: content.data.description,
            population: content.data.population
        };
    
        // Specify the view file to use
        var view = resolve('country.html');
    
        // Return the merged view and model in the response object
        return {
            body: thymeleaf.render(view, model)
        }
    };

    The part controller file above handles the GET request and passes the country content data to the view file which is shown below.

    Country part view:
    <div>
        <h3 data-th-text="${name}"></h3>
        <div data-th-if="${population}" data-th-text="'Population: ' + ${population}"></div>
        <div data-th-if="${description}" data-th-text="${description}"></div>
    </div>

The Hello Region Page

Parts start to make sense when placed into a region. Regions are “slots” contained within pages or layouts. Pages and layouts may contain multiple regions, and each region must have a unique name.

Let’s create a new page component with a single region called Main. Later we will use the Content Studio to place the Country part into this region.

The benefit of a region (see Regions) is that a page component can be re-used across multiple different pages by simply adding different parts to them as needed.

  1. Create a folder called hello-region in your project’s site/pages/ folder.

  2. Add the Hello region page descriptor, controller and view files:

    Page descriptor:
    <page>
      <display-name>Hello Region</display-name>
      <config/>
      <regions>
        <region name="main"/>
      </regions>
    </page>

    The XML file above is a Descriptor. Regions and page configurations can be defined here.

    Page controller:
    var portal = require('/lib/xp/portal'); // Import the portal library
    var thymeleaf = require('/lib/xp/thymeleaf'); // Import the Thymeleaf library
    
    // Handle the GET request
    exports.get = function(req) {
    
        // Get the content that is using the page
        var content = portal.getContent();
    
        // Extract the main region which contains component parts
        var mainRegion = content.page.regions.main;
    
        // Prepare the model that will be passed to the view
        var model = {
            mainRegion: mainRegion
        }
    
        // Specify the view file to use
        var view = resolve('hello-region.html');
    
        // Return the merged view and model in the response object
        return {
            body: thymeleaf.render(view, model)
        }
    };

    This page controller uses a portal library (see lib-portal in Javascript Libraries) to get the content and extract the main region which was defined in the descriptor XML file.

    Page view:
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello world</title>
    </head>
    <body>
        <h1>Country</h1>
        <div data-portal-region="main">
            <div data-th-if="${mainRegion}" data-th-each="component : ${mainRegion.components}" data-th-remove="tag">
                <div data-portal-component="${component.path}" data-th-remove="tag"></div>
            </div>
        </div>
    </body>
    </html>

    The view file above defines the place on the page where the region will render component parts that are dragged and dropped in the Page Editor.

  3. When done - redeploy your app once again!

    ./gradlew deploy
You can restart XP in Development mode and then the app won’t have to be redeployed after making changes.

Add your favorite country

Now that the Country content type is installed (and we have a part to display them), we can create new countries using the Content Studio interface.

  1. Right-click on the Hello World site from the navigation tree and select New. The Create Content dialogue will open.

  2. Select Country from the list of content types.

  3. Fill in the form with the details of your favorite country.

Similar to the site, we must also configure a view for the country

  1. In the toolbar, in the top right corner of the page, click the Menu icon button to activate the Page Editor (panel on the right-hand side with blue background).

Country Content form
  1. In the Page Editor select Hello Region from the template selector dropdown. If the dropdown arrow is not visible, double-click inside the option field or start typing "hello world" in it to see the options.

The blue box with Drop here is the region that we defined in the project code and it’s where we add components here in the Page Editor.

  1. Click the Cog icon button in the toolbar to open the Inspection Panel (far right).

  2. In the Inspection Panel, click the Insert tab. This reveals a list of default component types that can be placed into regions.

  3. Click and drag a new part (Part icon) into the box on the page.

  4. A new dropdown option will appear. Select the country part.

  5. Save draft and close the content edit tab.

When you click on the country in the content pane, you should see a preview of the rendered page, something like this:

Country Content rendered

The Country Page Template

With our current solution, sadly, we would have to create a new page for every country we add. As this is not a very effective way of working with large data sets, we will create a page template that will automatically render all country content.

  1. Select the Templates item Templates icon located below the Hello World site in the content panel.

  2. Click New and select Page Template.

  3. Fill in the form as follows:

    . Display Name: Country

    . Supports: Country (select from the list of content types)

  4. If the blue Page Editor panel is not displayed on the right, click the Menu icon button in the toolbar.

  5. Select the Hello Region controller with the dropdown in the blue Page Editor panel.

  6. Open the Inspection Panel (activated with the Cog icon button in the toolbar).

  7. Under the Insert tab, drag and drop a new part (Part icon) into the empty region where it says Drop here.

  8. Select the country part from the dropdown.

  9. Click Save draft in the toolbar and close the tab.

Every Country content you create from now on will use this template by default.

The Supports property is the key. A page template will support rendering of the content types specified here.

Try this out by creating a few new countries in your site. Be aware that every content you create will be a child of the content that was selected in the content pane, so make sure you select the Hello World site before clicking New in the toolbar. Or better yet, get in the habit of right-clicking the desired parent content and selecting New from the context menu. This way you will never accidentally create a content in the wrong place.

Starting from Enonic XP 6.11, the Detail Panel on the far right of the Browse View has a little box showing the rendering mode. Compare your favorite country to other countries that are rendered automatically:
Rendering mode

Extra task

Make your Favorite Country use the page template too!

You might remember that your favorite country was “hardcoded” - so let’s change it to use templates as well.

  1. In the Browse View panel, double-click the country content to edit it.

  2. Open the Inspection Panel with the Cog icon button and select the Inspect tab if it’s not already selected.

  3. You should see a label for Page Template with Custom selected and a label for Page controller with Hello Region selected. If you see a label for Part instead then click on the page above the country name to select the page. Then click the Inspect tab. (See image below)

  4. Now select Automatic from under the Page Template label in the Inspect tab.

  5. Save draft and close the tab.

Automatic Page Template

You can select another Page template at any time, or even customize the presentation of a single content.

Part 3: Configurable components

Welcome to part three. Here we will create a new content type for cities and a configurable component to display them. You will also learn how to make app configurations and you will get more practice with the Content Studio. Part three will conclude with content publishing and some advice and best practices.

Hello Geo World

The home page of the site now shows a list of the countries we have added. To make this even more exciting, we will add a City content type with geo-location and a City list part with configuration capabilities.

City content

The next steps will create a content type for adding cities with location coordinates.

  1. Create a folder named city inside the project’s site/content-types folder.

  2. Add the content type file below to your project. The file must be named city.xml, in accordance with the content type’s folder name (city).

    City content type:
    <content-type>
      <display-name>City</display-name>
      <super-type>base:structured</super-type>
      <form>
        <input type="GeoPoint" name="location">
          <label>Location</label>
          <occurrences minimum="1" maximum="1"/>
        </input>
        <input type="TextLine" name="population">
          <label>Population</label>
          <occurrences minimum="0" maximum="1"/>
        </input>
      </form>
    </content-type>

    The file above defines a content type for cities with a required field for the location in latitude and longitude.

  3. Copy the image below and save it in the same folder with the City content type. Name it city.png.

City content type

City list part

We need a part component to display the city data. It will list the cities and show a Google map of each location.

  1. Create a folder named city-list inside the project’s site/parts folder.

  2. Add the part descriptor file. It must be named city-list.xml.

    City list part descriptor:
    <part>
      <display-name>City list</display-name>
      <config>
        <input type="ComboBox" name="mapType">
          <label>Map type</label>
          <occurrences minimum="0" maximum="1"/>
          <config>
            <option value="ROADMAP">ROADMAP</option>
            <option value="SATELLITE">SATELLITE</option>
            <option value="HYBRID">HYBRID</option>
            <option value="TERRAIN">TERRAIN</option>
          </config>
        </input>
        <input type="TextLine" name="zoom">
          <label>Zoom level 1-15</label>
          <default>10</default>
          <config>
            <regexp>\b[1-9][0-5]?\b</regexp>
          </config>
          <occurrences minimum="0" maximum="1"/>
        </input>
      </config>
    </part>

    The part descriptor above has a configuration similar to those found in content types.

  3. Add the part controller file. It must be named city-list.js.

    City list part controller:
    var contentLib = require('/lib/xp/content'); // Import the content library functions
    var portal = require('/lib/xp/portal'); // Import the portal functions
    var thymeleaf = require('/lib/xp/thymeleaf'); // Import the Thymeleaf rendering function
    
    // Handle the GET request
    exports.get = function (req) {
    
        // Get the part configuration for the map
        var config = portal.getComponent().config;
        var zoom = parseInt(config.zoom) || 10;
        var mapType = config.mapType || 'ROADMAP';
    
        // Get the site configuration for the app
        var siteConfig = portal.getSiteConfig();
        var googleApiKey = siteConfig.googleApiKey;
    
        // String that will be inserted to the head of the document
        var googleMaps = '<script src="http://maps.googleapis.com/maps/api/js?key=' + googleApiKey + '"></script>';
    
        var countryPath = portal.getContent()._path;
    
        // Get all the country's cities
        var result = contentLib.query({
            start: 0,
            count: 100,
            contentTypes: [
                app.name + ':city'
            ],
            query: "_path LIKE '/content" + countryPath + "/*'",
            sort: "modifiedTime DESC"
        });
    
        var hits = result.hits;
    
        var cities = [];
    
        if (hits.length > 0) {
            googleMaps += '<script>function initialize() {';
    
            // Loop through the contents and extract the needed data
            for (var i = 0; i < hits.length; i++) {
    
                var city = {};
                city.name = hits[i].displayName;
                city.location = hits[i].data.location;
                city.population = hits[i].data.population ? 'Population: ' + hits[i].data.population : null;
    
                cities.push(city);
    
                if (city.location) {
                    city.mapId = 'googleMap' + i;
    
                    googleMaps += 'var center' + i + ' = new google.maps.LatLng(' + city.location + '); ';
    
                    googleMaps += 'var mapProp = {center:center' + i + ', zoom:' + zoom +
                                  ', mapTypeId:google.maps.MapTypeId.' + mapType + ', scrollwheel: false };' +
                                  'var map' + i + ' = new google.maps.Map(document.getElementById("googleMap' + i + '"),mapProp); ' +
                                  'var marker = new google.maps.Marker({ position:center' + i + '}); marker.setMap(map' + i + ');';
                }
    
            }
    
            googleMaps += '} google.maps.event.addDomListener(window, "load", initialize);</script>';
        }
    
        // Prepare the model object that will be passed to the view file
        var model = {
            cities: cities
        };
    
        // Specify the view file to use
        var view = resolve('city-list.html');
    
        // Return the response object
        return {
            body: thymeleaf.render(view, model),
            // Put the maps' javascript into the head of the document
            pageContributions: {
                headEnd: googleMaps
            }
        }
    };

    There are a few things to note in this controller file. It gets the part configuration for zoom and map type which were defined in the part descriptor. It also gets the site configuration for the Google Maps API key which we will define later in the site.xml file. Page Contributions are used to put the Google Maps JavaScript into the head of the document.

  4. Add the part view file. It must be named city-list.html to match the resolve function in the controller.

    City list part view:
    <div class="cities" style="min-height:100px;">
        <h3>Cities</h3>
        <div class="city" data-th-each="city : ${cities}">
            <h3 data-th-text="${city.name}"></h3>
            <div data-th-if="${city.population}" data-th-text="${city.population}"></div>
            <div data-th-id="${city.mapId}" data-if="${city.mapId}" style="width:100%;height:300px;"></div>
            <br/><br/>
        </div>
    </div>

    There is just one more file to edit and then all the code for this project will be complete.

App configuration

Google Maps won’t work without an API key. We need somewhere to enter the key so that it can be accessed by any component that needs it. The perfect place to define a site-wide configuration is in the site descriptor.

  1. Replace the contents of the site.xml file at site/site.xml with the code below.

    Site descriptor:
    <site>
      <config>
        <input type="TextLine" name="googleApiKey">
          <label>Google API key</label>
          <help-text>Get your Google maps Javascript API key at: https://developers.google.com/maps/documentation/javascript/get-api-key</help-text>
          <occurrences minimum="1" maximum="1"/>
        </input>
      </config>
    </site>

    The inputs defined in the site descriptor will appear in a form when editing the site content in the Content Studio.

  2. If you didn’t start XP in Development mode then build and deploy your project one final time with ./gradlew deploy.

All of the project’s files are now complete. The rest of the steps will be performed in the Content Studio interface.

  1. Edit the site content. You will notice that the Myapp application is now red due to a missing required configuration.

  2. Click the pencil icon to edit the Myapp configuration and enter your Google API key. You can get a Google API key from the Google developers site if you don’t already have one.

Myapp config

Create Cities

Now let’s make use of the new city content type and part component. First we need to add the City list part to the Country page template.

  1. Edit the Country page template. Find it in the content pane below the templates Templates icon

  2. Open the Inspect Panel by clicking the Inspect panel button in the toolbar.

  3. Under the Insert tab, click and drag a Part Part to the page region below the country part. (This may be a bit tricky because the country part is small.) You could also right-click on the word Country and then use the context menu to insert the part.

  4. Select the City list part from the dropdown in the box.

  5. Save and close the tab.

Now we need to create a few City contents under each country. (Sample data is available in the table below.)

  1. From the content pane, select a country content that you created earlier. It is important that each city content is created under its country.

  2. Right-click the country content and select New. The Create content dialogue will open.

  3. Now select City from the list of content types.

  4. Fill in the city name and location; the population is optional. The location format must be comma separated latitude and longitude with decimals.

  5. Save draft.

  6. Create several more city contents below each country content by repeating the previous steps. Sample data is provided in the table below.

Sample city data

Each country page will now have a list of the cities you created with a Google map of the location. It should look something like this:

City list

Configure City List

The City list part descriptor (site/parts/city-list/city-list.xml) has configuration inputs for the map type and zoom level. You can set the default values for these inputs by editing the City list part in the Country page template.

  1. Open the Country page template for editing.

  2. Open the Inspection Panel by clicking the Inspect panel button in the toolbar.

  3. Click on the City list part in the Page Editor panel.

  4. Under the Inspect tab of the Inspection Panel, set the Map type to HYBRID and Zoom level to 12.

  5. Save draft and close the edit tab.

Now all of the countries will show the city maps with the new settings. You can override these defaults for any individual country by editing the Country content and changing its City list part configuration.

City list

Go Online

Now that your Hello World site is complete, it’s time to publish.

  1. Select the Hello World site in the content browse panel

  2. Right-click and select Publish from the context menu, or click the Publish button in the toolbar

  3. The Publishing Wizard will appear. You may click the Child items icon to see all child nodes that will also be published.

  4. Verify that all your items are listed and click Publish!

When publishing content, all the selected items and changes are “cloned” from draft to the master branch (Repository).

You will always see the draft version of content in the preview panel and the Page Editor of the Content Studio. If you have placed your site on root level, you can also see your live site at this url: http://localhost:8080/portal/master/hello-world.

Well done, you have just created your first site for Enonic XP! The Enonic team congratulates you - we look forward to seeing all the brilliant things you will make.

We are always looking for feedback.

Now take a look at some Pro Tips.

Pro Tips

Adding libraries to your project

Why re-invent the wheel for each app you make? Reusable code can be created in libraries and added to any XP project. Some essential tools can be found in the Util lib, Menu lib, RECAPTCHA and Landing page libraries.

Adding apps to your site

A website can be created from a single app. But a site’s functionality can be extended by adding other pre-made apps. SEO Meta Fields, Disqus comments and Google Analytics are just a few of the many apps that can instantly add features to your site. See what’s available at the Enonic Market.

Handling Multiple projects

A best practice for working on multiple projects would involve keeping a separate XP_HOME folder for each project. The folder structure for such a setup would look something like this:

/Users/<name>/development
/Users/<name>/development/software/<xp-install-version>
/Users/<name>/development/xp-homes/<project-name>/home
/Users/<name>/development/projects/<project-name>/<project-source-files>

An actual implementation with projects called my-first-app and company-site would look like this:

/Users/mla/development/software/enonic-xp-6.14.1
/Users/mla/development/xp-homes/my-first-app/home
/Users/mla/development/xp-homes/company-site/home
/Users/mla/development/projects/my-first-app/...
/Users/mla/development/projects/company-site/...

This allows you to have one Enonic XP installation for each version and as many different XP_HOME folders as you need for your projects. When switching from one project to another, you only have to change the XP_HOME environment variable and then restart the installation of the Enonic XP version that the project was created for.

Check this Enonic Labs article for a more in-depth process. It also includes some bash scripting that will help with setting and changing $XP_HOME and starting and stopping XP.

Logging JSON objects

While developing an app, it can be helpful to see the structure of objects returned by library functions. The best way to do this is to set up a utilities JavaScript file in the project lib folder. Add the following function to the utilities file:

site/lib/utilities.js
exports.log = function (data) {
  log.info('Utilities log %s', JSON.stringify(data, null, 4));
};

Call the log function in any controller like the example below and then check the log after refreshing the page.

var util = require('utilities');
var content = portal.getContent();
util.log(content);

This logging function and many other useful functions are included in the Util library.

Continuous build with Dev Mode or Gradle

It can be quite time consuming to frequently use the terminal to redeploy an app during development. Starting Enonic XP in dev-mode will make most changes to your app code visible immediately on localhost. See the Development mode page for more information about its capabilities and limitations.

Another option is to use ./gradlew -t deploy in the terminal from the project root. This will automatically build and redeploy your app every time changes to a file are detected. This method is slower than dev-mode, but it handles some situations that dev-mode doesn’t. Gradle 2.7 or newer is required.

Both dev-mode and Gradle continuous-mode require the $XP_HOME environment variable to be set in the terminal window.

Next Steps

This tutorial only covered the basics of app development. Explore the documentation for more in-depth coverage.

Check our GitHub page for examples of more advanced apps. The Superhero blog app is also a good place to see more advanced code. The Google Analytics app demonstrates extending the Content Studio interface.

The HTML files in this tutorial used Thymeleaf for dynamic data. Visit the Thymeleaf documentation to learn more about it.

Need help? Ask questions on our forum or answer questions from others.

Contents