Loading...

Thursday, March 31, 2011

Grails Goodness: Applying Layouts in Layouts

Grails uses Sitemesh to support view layouts. A layout contains common HTML content that is reused on several pages. For example we can create a web application with a common header and footer. The body of the HTML pages is different for the pages in the web application. But the body can also have a common layout for certain pages. For example some pages have a body with two columns, others have a single column. To reuse these layouts we can use the <g:applyLayout .../> tag. This means we can apply layouts in layouts, which provides a very flexible solution with optimal reuse of HTML content.

Update: another way to apply layouts in layouts based on the comments by Peter Ledbrook.

Let's see how this works with a small sample application. All pages have a header with a logo, search form and main menu. Each page also has a footer with some copyright information. The homepage has a body with five blocks of information, we have product list page with a one column body and finally a product details view with a two column body.

The following diagrams shows the structure of the pages:

Layout homepage

Layout product list

Layout product details

First we create the main layout with the header and footer HTML content. The body content is variable for the pages, so we use <g:layoutBody/> in our main layout.

<%-- File: grails-app/views/layout/page.gsp --%>
<!DOCTYPE html>
<html>
<head>
    <title><g:layoutTitle default="Grails"/></title>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}"/>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'layout.css')}"/>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'fonts.css')}"/>
    <link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
    <g:layoutHead/>
</head>
<body>
<div id="header" class="clearfix">
    <div id="logo">
        <g:link uri="/"><g:message code="nav.home"/></g:link>
        <p><g:message code="title.website"/></p>
    </div>

    <div id="searchform">
        <g:form controller="search">
            <fieldset class="search">
                <input type="text" class="search-input" value="${message(code:'search.box.search')}" name="search" id="search-phrase" maxlength="100"/>
                <input type="submit" value="${message(code: 'search.box.submit')}" />
            </fieldset>
        </g:form>
    </div>

    <div id="navmenu">
        <ul>
            <li><g:link uri="/"><g:message code="nav.home"/></g:link></li>
            <li><g:link controller="product" action="list"><g:message code="nav.products"/></g:link></li>
        </ul>
    </div>
</div>

<g:layoutBody/>

<div id="footer">
    <p>Copyright © 2011 Hubert A. Klein Ikkink - <a href="http://www.mrhaki.com">mrhaki</a></p>
</div>
</body>
</html>

For our homepage we define a layout with five blocks. We use Sitemesh content blocks in our layout. This way we can define content blocks in the pages and reference these components in the layouts. To reference a content block we use the <g:pageProperty .../> tag. The name attribute contains the name of the content block we want to reference. Notice we reference our main layout page with <meta name="layout" content="page"/>.

<%-- File: grails-app/views/layout/fiveblocks.gsp --%>
<html>
    <head>
        <meta name="layout" content="page"/>
        <title><g:layoutTitle/></title>
        <g:layoutHead/>
    </head>
    <body>
        <div id="banner">
            <g:pageProperty name="page.banner"/>
        </div>

        <div id="left">
            <g:pageProperty name="page.left1"/>
            <g:pageProperty name="page.left2"/>
            <g:pageProperty name="page.left3"/>

            <div id="box-left">
                <g:pageProperty name="page.box-left"/>
            </div>

            <div id="box-right">
                <g:pageProperty name="page.box-right"/>
            </div>
        </div>

        <div id="right">
            <g:pageProperty name="page.right1"/>
            <g:pageProperty name="page.right2"/>
            <g:pageProperty name="page.right3"/>
        </div>
    </body>
</html>

And in the homepage Groovy server page we can use the <g:applyLayout .../> tag and define the content for our Sitemesh content blocks.

<%-- File: grails-app/views/templates/homepage.gsp --%>
<g:applyLayout name="fiveblocks">
    <head>
        <title><g:message code="title.homepage"/></title>
    </head>

    <content tag="banner">
        <h1>Welcome to Grails Layout Demo</h1>
    </content>

    <content tag="left1">
        <p>...</p>
    </content>

    <content tag="box-left">
        <p>...</p>
    </content>

    <content tag="box-right">
        <p>...</p>
    </content>

    <content tag="right1">
        <p>...</p>
    </content>
</g:applyLayout>

We also define a layout with one block:

<%-- File: grails-app/views/layout/oneblock.gsp --%>
<html>
    <head>
        <meta name="layout" content="page"/>
        <title><g:layoutTitle/></title>
        <g:layoutHead/>
    </head>
    <body>
        <div id="main">
            <g:pageProperty name="page.main1"/>
            <g:pageProperty name="page.main2"/>
            <g:pageProperty name="page.main3"/>
        </div>
    </body>
</html>

And a layout with two columns:

<%-- File: grails-app/views/layout/twoblocks.gsp --%>
<html>
    <head>
        <meta name="layout" content="page"/>
        <title><g:layoutTitle/></title>
        <g:layoutHead/>
    </head>
    <body>
        <div id="left">
            <g:pageProperty name="page.left1"/>
            <g:pageProperty name="page.left2"/>
            <g:pageProperty name="page.left3"/>
        </div>

        <div id="right">
            <g:pageProperty name="page.right1"/>
            <g:pageProperty name="page.right2"/>
            <g:pageProperty name="page.right3"/>
        </div>
    </body>
</html>

Then we use these layouts in the product list and details views:

<%-- File: grails-app/views/templates/productlist.gsp --%>
<g:applyLayout name="oneblock">
    <head>
        <title><g:message code="title.product.list"/></title>
    </head>

    <content tag="main1">
        <h1><g:message code="products.list"/></h1>
        <ul class="product-list">
        ...
        </ul>
    </content>
</g:applyLayout>
<%-- File: grails-app/views/templates/productview.gsp --%>
<g:applyLayout name="twoblocks">
    <head>
        <title>${product.title}</title>
    </head>

    <content tag="left1">
        <h1>${product.title}</h1>

        <p class="product-body">...</p>
    </content>

    <content tag="right1">
        <p>...</p>
    </content>
</g:applyLayout>

Because this is only a small sample application this might seem like overhead, but for a bigger application it is really useful to have reusable layouts. The following screenshots show the layouts in action for a Grails application.

Layout homepage

Layout product list

Layout product details

Sources of the sample Grails application can be found at GitHub.

Tuesday, March 29, 2011

Grails Goodness: Splitting i18n Message Bundles

Grails supports internationalization out of the box. In the directory grails-app/i18n we find a messages.properties with default Grails messages. We only have to make a new properties file where the name ends with the locale to add messages for another locale to our application.

But we are not restricted to the basename messages for our message bundles. We can use any name as long as we place the file in the grails-app/i18n directory. For example we can create a file views.properties to store messages related to the Groovy Server Pages and a file validation.properties with messages related to validation of domain objects. And if we want specific messages for the Dutch locale for example we create views_nl.properites and validation_nl.properties.

Monday, March 28, 2011

Use Alfresco's Web Editor with Different HTTP Port Number

Alfresco's Web Editor is a Spring Surf application that can deployed next to an Java application that uses Alfresco's Web Quick Start. The Java application must be configured correctly to allow inline editing of content with the Web Editor. But also the Web Editor application must be configured to make it all work.

A common exception when logging in via the Web Editor Javascript login dialog is:

ERROR [/awe].[Spring Surf Dispatcher Servlet]  - Servlet.service() for servlet Spring Surf Dispatcher Servlet threw exception
java.lang.NullPointerException
     at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:768)
     at org.springframework.extensions.surf.cache.ModelObjectCache.get(ModelObjectCache.java:82)
     at org.springframework.extensions.surf.cache.ModelObjectCache.get(ModelObjectCache.java:41)
...

If we have installed Alfresco and set the HTTP port for Alfresco to something else than port 8080, then we must also change the configuration for the Web Editor application. We can find the configuration in the following file: $ALFRESCO_HOME/tomcat/shared/classes/alfresco/web-extension/awe-config-custom.xml. We must open this file and add the following to the alfresco-config root element:

<config evaluator="string-compare" condition="Remote">
    <remote>
        <endpoint>
            <id>alfresco</id>
            <name>Alfresco - user access</name>
            <description>Access to Alfresco Repository WebScripts that require user authentication</description>
            <connector-id>alfresco</connector-id>
            <endpoint-url>http://server:port/alfresco/s</endpoint-url>
            <identity>user</identity>
        </endpoint>
    </remote>
</config>

We must set a correct value for the endpoint-url element to reflect the servername and port number of our Alfresco installation. Once we have changed the file and saved our modifications we can restart the application server and we should be able to login in to Alfresco and use the Web Editor in our Java application.

Grails Goodness: Access Configuration in Grails Scripts

We can create our own scripts in Grails that can be executed from the command-line. To access values from the properties we have defined in grails-app/conf/Config.groovy we must start with adding the following line to the top of our script:

includeTargets << grailsScript('_GrailsPackage')

With this include we get access to the checkConfig task.

Next we must execute this task after the compile task to get a config variable in our script. The script variable config contains the values of our configuration defined in the Grails configuration files.

The following script contains some sample configuration properties for different environments.

// File: grails-app/conf/Config.groovy
...

blog.sample = 'Blog sample'
environments {
    development {
        blog.sample = 'Value for development'
    }
}

...

Let's create a new script Sample.groovy with the following command: $ grails create-script sample. We open the file and add:

// File: scripts/Sample.groovy
includeTargets << grailsScript('_GrailsPackage')

target('sample': 'Show usage of configuration information in Grails scripts.') {
    depends(compile, createConfig)

    println 'Sample = ' + config.blog.sample
}

setDefaultTarget 'sample'

If we execute our task with $ grails sample we get the following output:

Sample = Value for development

And if we run $ grails test sample we get:

Sample = Blog Sample

The original source for this information is Grails mailing list.

Tuesday, March 15, 2011

Alfresco Installation and Missing db_setup.sql File

If we install Alfresco and want to initialize a MySQL server with the correct database name and user we must execute run a file db_setup.sql. This file should be in the Alfresco installation directory, but in my Linux and Mac OSX installation directories I couldn't find the file. Searching on the internet I found what the contents of the file must be:

create database alfresco default character set utf8 collate utf8_bin;
grant all on alfresco.* to 'alfresco'@'localhost' identified by 'alfresco' with grant option;
grant all on alfresco.* to 'alfresco'@'localhost.localdomain' identified by 'alfresco' with grant option;

We can execute this script with $ mysql -u root -p < db_setup.sql and a database with the name alfresco is created, together with a user alfresco and password alfresco.

The first time we start Alfresco after installation the correct tables and contents is created in this new database.

Thursday, March 10, 2011

Set Default Remote Repository for Mercurial Push and Pull

I wanted to use a remote bitbucket repository for pushing changesets to an already locally created Mercurial repository. Normally we can use $ hg push https://mrhaki@bitbucket.org/mrhaki/com.mrhaki.sample-app. But if we want to use $ hg push without the repository URL we must add the URL to the file $REPO/.hg/hgrc. We add the following contents:

[paths]
default = https://mrhaki@bitbucket.org/mrhaki/com.mrhaki.sample-app