For the last few months, I have been working to develop a Grails-based CMS system to enable the management of an existing website.
In this post, I will outline some of the high-level implementation details and some of the strategies I took. I will also outline some of the tools and plugins used to build this complete CMS system. The overall time it took to develop this entire system in Grails was about five to six weeks, with interruptions to continue developing other projects and a holiday break. I hope this bird’s eye view is helpful for whoever desires to build a similar system. Feel free to contact me for any details or implementation questions.
Bird’s Eye View
Note: I did not skin these pages – this was done by an employee of the company. I’ve also removed the company name to comply with potential NDA issues.
(click image for higher resolution detail)
Pages, Sidebars and Sections
For this application, I divided up individual pages into pages, which had a one to many relationship to sections and sidebar elements. Each of these items could be edited and previewed. I also modified the PageController and the UrlMappings file so that http://www.xxx.com/pageName resolves the page or carrier name ( since my client is a mobile provider ) . This allowed us to make changed quickly to static pages and allow people without much technical knowledge to modify these pages.
I also had to modify the renderEditor templates so that, in addition to displaying a Create Link, it also provided a checkbox list of Sections and sidebars so that the content administrator could change the sections of content visible and available to each page.
Outside of traditional pages that had a pre-selected list of information, our client also had different content that needed to be formatted in a certain way. This included Management Bios, Press Releases, Jobs, News Items, Product Details, Procurement details, Phone models, Widgets, etc. I basically enabled them to manage their own product catalog in a way that used their own language, instead of trying to make them accommodate to the language of the CMS. In my opinion, here is where Grails ( or Rails or Pylon ) shines compared to a traditional CMS system, as it allows you to build a custom edit screen quickly for each of these model items.
Given than I was pulling in data from an existing product database, I used Burt Beckwith’s excellent Datasources plugin to connect proxy domain objects that I wrote to manage product information.
I built a custom navigation tree structure – very similar to the Navigation Plugin ( except that this wasn’t available 3 months ago ). Our structure was very simple, a navigationLink domain object could be either a child of a header or footer navigation, or another navigation link. The links generated could either link to a page or an url could be typed.
I refrained from using the Grails g:link paradigm because the end user did not really understand what a controller or action would be. Instead, I provided a dropdown of all possible pages and a box where they could enter an URL ( since the app linked to an external java app ).
Whenever each of these navigation links were saved, I persisted the view to disk into a GSP view. Since this navigation was also used by a wordpress site and a forum, I extended the templating engine to write out a PHP header and footer as well whenever URLs were modified.
Finally, I included a simple action called ‘generate’ to enable the transition between servers based on the server.url parameter in the server configuration.
The final piece of this CMS puzzle was permissions and role management. We simply plugged in JSecurity, set up the filters and walked away!
Given that the system had many users, we built a filter mechanism to search for users based on attributes. This is definitively something that needs to go into views early, and I would even consider making the Filter plugin a default in my page templates.
1. Lock down functionality that goes into templates early
One of the more frustrating aspects of this project for me was going back and editing controllers to add functionality like preview or one-to-many relationships management after views and controllers have been generated. I also made the mistake of updating to Grails 1.0.4 in the process, which changed the way variable instances were referenced in generated views from Book to BookInstance. This meant a lot of refactoring and global code replacing in controllers and domain objects during development.
For future projects, it makes sense to look at the required functionality and start by editing the Grails templates even before starting a project. Here are some considerations:
- Change the way that templates manage 1 to m relationships according to your needs by changing the RenderEditor.groovy file. Test.
- Consider adding dateCreated and lastUpdated fields to your domain object template. Also remember to set the display constraint of these properties to false. Grails automatically manages these properties for you, and it makes migrating content from one server to another much easier. This is specially useful when dealing with a CMS system where there might be content added to a live site and one to staging.
- Draft and Preview functionality should be built into EVERY domain object.
2. Basic HTML is not enough
Grails templates are very powerful, but they sometimes introduce limitations to the workflow of editing a page. One of the more irksome problems I had with a pure page redirect approach to editing was the loss of context. When adding a navigation link, it would be nice to see how it fits within a tree. When adding a section to a page, it would be nice to have that section show up right on the page.
A possible solution would be extensive use of AJAX and the modalbox plugin, templating large sections of site creation with these tools ( this is the approach we took with the Refund Roadtrip. However, this removes much of the automated CRUD abilities of Grails out of the box ( or maybe not, see 3 ).
The more radical solution, and one that I would actually recommend, would be to use Adobe AIR or Flex ( Or JavaFX, Silverlight, Visual Basic, etc ) to manage some of the rich editing functionality of the page. If I was to write this project again, I would have written the page management and navigation editing components in AIR. I see four advantages to this solution:
- AIR has a built in HTML browser and Rich Text Editor, which would have simplified writing Dreamweaver Lite.
- External file support: move editing to the desktop and you can now reference images and create pdf links to local files and upload an entire page as bundle, as opposed to the piecemeal approach where you have to make sure the image you changed is in the server. It makes the transaction more atomic.
- Drag and Drop: Drag images into HTML area to build a link. Drag elements from a tree around to change navigation.
- Simplified Preview: By pushing down the preview functionality into the client, we can simply get a resolved GSP template page with placeholder XML, replaced with the local HTML content
3. Get a better templating plugin
We noticed halfway through the project that we were building many of the same display pages over and over again. The default Grails scaffolding templates only handle the CRUD templates, if I was to start another CMS project, I would use another system such as the xTemplates plugin.
4. Database migration takes time
Database support is much easier with Grails and the myriad of plugins available, but it is still not perfect. For this project, we used the Bootstrap to lock down initial data, and then used the Liquibase plugin to manage structural changes in the database.We also used the Datasources plugin to manage our different databases.
For more recent projects, I have started using the Fixtures plugin, but I lose the ability to create domain objects based on other data that might not be inside fixture declarations. For example, for this project, I had to define new carrierInfo objects based on Carriers that were already in the system. Doing that in the boostrap files might have been easier than trying to define fixtures for it.
Because CMS content tends to be very verbose, I often ran into SQL errors where the content I was trying to insert was too large for MySQL. This was solved by mapping description and content columns as ‘text’ in mySQL, but it definitively is something that usually escapes the mind.
5. Need a better strategy for version control
To simplify the timeline of this project, the only type of content version controlling we implemented was to use a staging and a production server, where a content team would manage the website and push these changes to a live site.
We did add a draft / published functionality for ‘one time edit’ items like jobs or news items, but a real Content Management system should have a well-thought out workflow and change tracking infrastructure.
Possible solutions include using a real content repository like JackRabbit or Alfresco. In Grails 1.1, it will be possible to move away from GORM and use a Java Content Repository, and this seems to be possible with Grails plugins, but I have not looked too far into this.
Another solution is to bake in version and source controlling into the Grails application itself. This is the approach taken by the good folks at Grails.org. Each page edit is tracked in the doman object as an active version.
There is a problem that I still have to find an elegant solution to is the versioning of relationships, a Page could have been saved with sidebar items 23,43,12 during iteration 2, and 23,14 during iteration 2. For this project, I serialized the relationship into a string, which I then stored with the given version and passed to the sidbar and section templates for rendering. This is not elegant, but it works. One could potentially also store version numbers in these relationship strings, but this would depend.
6. Starting from scratch is not that hard
One of the myths about starting from scratch with a CMS is that it takes too long. But given the robust ecosystem of plugins in Grails and automated scaffolding, the bulk of the time with CMS projects like this is actually spent converting the initial static data into dynamic data, modifying templates and integrating with existing systems. I would say that if you gave me a set of HTML pages with content that needed to be versioned and managed, I could give you a CMS for it in less than 3 days.
7. Optimize by building a self-scraping site
So far, I have built six Grails-based CMS enabled applications. One of the issues that always arises is that of performance and the need to deploy into a server to manage the CMS. Since a lot of our content is static, we could really remove some of the Grails powered GSP sites by just dumping out the flat HTML pages into an Apache root.
We could also dump out flat XML files and then use the Flex plugin’s dynamic compilation ability to build rich media content into SWFs that don’t rely on a web service to obtain their data.
The way I would approach this is as follows: add hibernate event listeners to insert, save and delete functionality of the domain object template so it serialized to disk changes to the class. We do this for our CMS for the sidebar objects, since they are views that have Grails code — but there is no reason that flat HTML files can be obtained easily via page scraping. This is also the approach I would use to generate sitemaps.
Why not Alfresco, Joomla, ExpressionEngine or Drupal?
The advantage that Grails and other web frameworks offer to content management is that you get to build the functionality from the ground up. This means that instead of trying to extend or trim down the general case Content Management applications, you get to choose precisely what kind of functionality goes into your CMS. This allows for flexibility and ease of development, since you are not bound to decisions made with other use cases in mind. These benefits must be weighted against the time it takes to develop it. If all your client wants is a forum, a blog and a page editor, the ExpressionEngine might make more sense.
I think the issue is not how you manage your content, but what you do with this content after it has been posted. After all, content management systems are no use if the final result provides of no use to the user. With a tool like Grails, you have a lot more versatility when designing the functionality of the web application that you ultimately create. While this project was only about building a CMS, I was able to add content filtering, programmatic integration, widget commenting and rating to the core website without much effort. Grails makes icing the cake much easier.