Content Management System (CMS) functionality for Grails apps

Recently I had to add CMS functionality to a Grails application.
The customer didn’t need any workflows, he just wanted to be able to create and edit content (pages and page fragments) online.
The advantage of this functionality is that pages can be changed without requiring a new release / deployment of the application.
If you need more complex CMS functionality like workflows and versioning, you should check out the Weceem Grails plugin.

This blog post will show you how you can create CMS functionality on your own with just a few classes. We will reuse Grails build-in GroovyPagesTemplateEngine – the same class that renders normal Groovy Server Pages / Grails views.
As a result, you will be able to create, edit and delete CMS pages (with a custom layout or using your applications layout) and page fragments (part of a page that can be integrated into other pages). The CMS pages can contain executable code, so you can create dynamic pages and reuse functionality your application already offers. Page rendering will be very fast because all CMS pages will be compiled. They will be as fast as normal GSP pages.

We start with a domain class that will persist our CMS pages.

class CmsPage {
  String pageId
  String content

  static constraints = {
    pageId(nullable:false, blank:false, unique:true, matches:"[a-zA-Z\._-]+")
    content(nullable:false, blank:false)
  }

  static mapping = {
    content(type:"text")
  }
}

The CmsPage domain class has a String page id that is needed to reference and render specific pages. Additionaly, it has a content String field that will hold the page’s sourcecode.

I’m not going to show you the CRUD controller, service and views here. It’s just the same as with any other Grails domain class.

Next, create a class “CmsPageRenderer ” in src/groovy that will render the CMS pages:

class CmsPageRenderer {

	GroovyPagesTemplateEngine groovyPagesTemplateEngine
	private Map pageTemplateCache = new HashMap()

	String renderPage(String pageId, Map model){
		StringWriter sw = new StringWriter()
		PrintWriter pw = new PrintWriter(sw)
		String pageName = pageId+'.gsp'
		Template template = pageTemplateCache.get(pageId)
		if(!template){
			CmsPage cmsPage = CmsPage.findByPageId(pageId)
			if(!cmsPage){
				throw new CmsPageDoesNotExistException(pageId)
			}
			template = groovyPagesTemplateEngine.createTemplate(cmsPage.content, pageName)
			pageTemplateCache.put(pageId, template)
		}
		template.make(model).writeTo(pw)
		return sw.toString()
	}

	void removeCmsPageFromCache(String pageId){
		pageTemplateCache.remove(pageId)
	}
}

GroovyPagesTemplateEngine is the same class used by Grails for GSP View rendering. We will reuse this class for the rendering of the CMS pages. The Map pageTemplateCache is a cache for the compiled GSP pages so that we only have to compile it once.

The renderPage() method will try to look up the CMS page in the cache. If the page is not found in the cache, the pages will be loaded from the database, compiled and put into the cache. Afterwars, the page will be rendered to a String using the given model.

The method removeCmsPageFromCache(String pageId) is used to remove a compiled CMS page from cache. You have to call this method whenever you update or delete a CMS page in your CRUD controller/service. Otherwise, modifications to CMS pages wouldn’t be visible because the cached pages would be used for the rendering.

The Exception CmsPageDoesNotExistException is thrown when you try to render a page that doesn’t exist.

class CmsPageDoesNotExistException extends RuntimeException {

  private String pageId

  CmsPageDoesNotExistException(String pageId){
    super("CMS page with Page-ID "$pageId" does not exist".toString())
    this.pageId = pageId
  }

  String getPageId(){
    return pageId
  }
}

Add the following code to your “resources.groovy” Spring config file to make the CmsPageRenderer available as a Spring bean:

cmsPageRenderer(CmsPageRenderer){
  groovyPagesTemplateEngine = ref('groovyPagesTemplateEngine')
}

Now that it is possible to render CMS pages, we need a way to trigger the rendering and to send the result to the browser. Create a controller named “CmsPageRenderingController”.

class CmsPageRenderingController {

  CmsPageRenderer cmsPageRenderer

  def renderPage = {
    assert params.pageId

    try {
      render(cmsPageRenderer.renderPage(params.pageId, params))
    } catch(CmsPageDoesNotExistException e){
      response.sendError(404)
      return
    }
  }
}

The action “renderPage” uses the service described earlier to render that page. All parameters submitted from the browser are passed the the service as a model. If the CMS page does not exist, a 404 error will be send to the browser. Now you are able to render complete CMS pages.

If you want to be able to render CMS page fragments and include them in other pages, create a Taglib named “CmsTagLib”.

class CmsTagLib {
  CmsPageRenderer cmsPageRenderer

  static namespace = 'cms'

  def renderCmsPage = { attrs, body ->
    if(!attrs.pageId){
      throw new IllegalArgumentException('pageId must not be null')
    }
    out << cmsPageRenderer.renderPage(attrs.pageId, attrs)   }   def cmsPageLink = {attrs ->
    out << createLink(controller: 'cmsPageRendering', action: 'renderPage', params: &#91;pageId: attrs.pageId&#93;)
  }
}
&#91;/sourcecode&#93;

The tag "renderCmsPage" will render the page fragment with the given pageId. The tag "cmsPageLink" will create a link to a CMS page that you can embed into other pages.

That's it. As mentioned earlier, you only have to add the CRUD GUI/logic for managing the CmsPage instances.

Here are some examples for CMS pages:

&#91;sourcecode language="html"&#93;<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>CMS page using custom layout.</title>
</head>
<body>
CMS page using custom layout.
</body>
</html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main"/>
<title>CMS page using your application's main layout</title>
</head>
<body>
CMS page using your application's "main" layout.
</body>
</html>
<p>This is a page fragment.</p>
Short URL for this post: https://wp.me/p4nxik-dD
This entry was posted in Groovy and Grails and tagged , , , . Bookmark the permalink.