Site settings in Enonic 4.7 with STK

Posted by


With the help of Enonic STK and our best-practices, we will create something in Enonic 4 that will come natively in Enonic 5: Site Settings. Let the user be in control of all the labels, images, and other things you otherwise "hard-code" into the templates.

Enonic 5 is just around the corner. It's full of new features. One of many that I can see benefiting me a lot in building websites, is Site Settings. With these settings you can easily change and add text related to the site, from inside the admin interface. For instance, you can change phone numbers to be placed in the footer, extra text for the header, change logo, update the header background image, etc. There are many times when a client wants to change something that is normally "hard-coded" by the developer. With Site Settings, both developer and client will benefit greatly.

But, that is in Enonic 5. Our coming next generation CMS. But I have some good news: This is already possible in Enonic 4! You just have to think a bit outside of the box. Let me show you one of many different solutions to this (because you can solve this in many different ways, I will show you one of them).

The recipe

We need a few ingredients to cook our Settings-dinner:

  • Enonic STK
  • Enonic 4.7.8 (just download, it's open source)
  • 1 Enonic ContentType
  • 1 Enonic Content Archive
  • 1 Content
  • 2 Enonic Portlets
  • 2 XSLT files (for the Portlets)
  • 1 Enonic Page Template

Just mix and add salt. If that doesn't work, follow the instructions below.


So the first thing to do is to set up our Enonic ContentType which will control what fields Enonic Admin will display for us. I'll go ahead and set it up in Sublime Text 2 before copy and pasting the final result into Enonic Admin.

This is basic XML syntax for defining ContentTypes in Enonic. I assume that you understand the basics of developing for Enonic.

The settings content we can create now, will have a header, only for display and search purposes in our archive. It will then have the support for one small and one large image (we will use the large one on our front page, and the small one on our other pages). Furthermore, we set some text for the footer, including e-mail and telephone number. As an extra bonus we can control a site-wide message on and off.

We could of course add a lot more here, add validation, and much more. But let's skip that in this lesson.

Content Archive

As with all Content in Enonic ,we need to put it into our Content Archive first. Just open up an existing Content Repository in the archive and click the "Edit content repository" button up top. Move your new ContentType over to the list of allowed types in this repository.

Now save and then enter this repository and add a Content Category (a folder). Set it up for your new ContentType, and save.

After this, you can go into this new folder and just click the "New" button and select "Settings (content)" from the drop down and you will be creating content of the type Settings.

Create one content of this type. Just fill in whatever you'd like in the fields. When it's time to save, you will not - as we normally do - publish the content. You will only "save and approve" it. That's it! This is because we're gonna use a special DataSource query later.

Preparing Portlets: XSLT

Now that we have our settings laying there waiting for us, we need to fetch them and display them. For that we need a Portlet (same as a Module, Widget, or whatever you like to call it). I could have done this directly in a Page Template, but because I have our STK in place (which handles Portlet rendering) it won't work so good with what I have in mind. Also, if the site has many different Page Templates you would have to edit all of them, and that's not so handy.

First thing to do when creating Portlets is to start working on that XSL-code. Just navigate to our STK (that you've of course already downloaded to your desktop) and duplicate the folder "module-sample-site" in folder "/modules". Rename this folder to "module-settings". Then duplicate the file "sample-module.xsl" inside it, and rename it to "settings-header.xsl" and the other file to "settings-footer.xsl".

You should now have one (1) folder named "module-settings" in the root of the STK "modules" folder. In that folder you should have two XSL files, one for header, and one for footer.

This is a standard Enonic "best practice" naming convention, you are however free to arrange things as you like.

Open the XSL code for "settings-header.xsl" in your favourite editor and replace it all with this:

The parameter "frontpage" in the top is for later. With it we can control if we're to use the large or small background image. In this article we will only use the small one, but this is an example of how this could be solved. With another Portlet overwriting this param we will get a different result. This could also be done in the XSL code by figuring out on which path level we're currently at in the menu. That would be the most optimal solution since that doesn't require duplicated Portlets.

Ofc our XSL won't handle any of the footer-data from the Settings content. We will however check if the checkbox for the sitewide message is checked, and if it is, we will display the message html. I could've just used an if that checked "is html empty", but that would force the user to always delete the entire message to deactivate it, and that is something that I think is not the optimal case for the user. Often they need to display the same message, but easily be able to turn it off and on again.

Go back to the folder where you put this file, and duplicate it. Name the new file "settings-footer.xsl". Open it, and replace the content with this code:

Basically, it just removed the logo and header image, and added some address data in the footer instead. Pretty basic stuff if you worked with XSL and Enonic before.

Portlet 1: "Header"

With the XSL-code in place we can go ahead and create our first Portlet in Enonic. Let's just name it "Header".  We'll then add our "settings-header.xsl" as stylesheet.

Now that that is done you can go to the tab "DataSource". Here we will write Enonic's own type of SQL - "EQL, Enonic Query Language". This datasource will also be written first in Sublime Text before it gets the copy pasta treatment into Enonic.

Normally we work with the "getContentBySection" query method. This however, forces you to publish the content to certain menuitems. And with settings, you want them to be active on every page all over the site. With "getContentByQuery" we can get this done easily. Just find the first and newest content of our type "Settings".

For the sake of scope, I left out the datasource that we would have needed to fetch our menu (getMenuBranch).

Another thing you might have noticed in the "query"-part of our DataSource is the "portal.locale"-thing. This is inserted to handle localization. With this added, we can simple create infinite amount of content, one per language in the world, and Enonic would automatically fetch the correct one for you.

Well, we're done here now so save this Portlet =)

Portlet 2: "Footer"

This one is easy:

Repeat exact same steps as before. Name the Portlet "Footer", and re-use the DataSource unchanged. Copy pasta - the best pasta!

Final showdown

We have our ContentType, we've created some Content of this type in our Content Archive. And we have created both our Portlets, with full XSLT code using Enonic STK.

To be able to publish this site and use it, we need to add these two Portlets to our Page Templates. As this is a clean install, we will create a new Page Template.

Go to the Page Templates page in Admin. Create a new template with the "page-default.xsl" template from our STK. This will work for now to display the site. Then add our two new Portlets to the "Main-region". Save and check your site. You should see all your settings in place - images, text, and other data.

(You don't need to add the ContentType "Settings" as an allowed contenttype on this Page Template since we won't publish anything to it because of our use of getContentByQuery.)

How neat!