Search code examples
javavelocity

VelocityEngine ignores default Layout Template


I have a velocity template for email notification, which looks like this:

#set( $layout = "base.vm" )
<tr>
    <td style="padding: 48px 48px 0px; line-height: 20px;">
        <h4 style="font-family: 'Proxima Nova', Arial, sans-serif;
                   font-size: 20px;
                   color: #21261f;
                   margin: 0px 0px 20px;
                   line-height: 24px;
                   font-weight: 700;">Notification</h4>

        Hi $user
    </td>
</tr>

I also have a reusable template which I want to use as a default layout for all templates:

<body style="font-family:'Proxima Nova',Arial,sans-serif; background-color: #f4f4f4; margin: 0; padding: 30px 0;">

<table style="margin: 0 auto; background-color: #ffffff; border-spacing: 0; min-width: 490px;">
    <tbody style="font-family: 'Proxima Nova',Arial,sans-serif; font-size: 16px; color: #303744;">
        $screen_content
        #parse("/snippets/footer_content_separator.vm")
        #parse("/snippets/footer_content.vm")
    </tbody>
</table>

</body>

I'm parsing my template in code like this:

        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "classpath");
        velocityEngine.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
        velocityEngine.init();

        Template t = velocityEngine.getTemplate("mail-templates/my_template.vm");

        VelocityContext context = new VelocityContext();
        context.put("user", "testing");

        StringWriter writer = new StringWriter();
        t.merge(context, writer);

For some reason after merging with StringWriter, it contains only my template content, without layout template content. I've tried to add layout template path to VelocityEngine like this:

velocityEngine.setProperty("tools.view.servlet.layout.directory", "/mail-templates/");
velocityEngine.setProperty("tools.view.servlet.layout.default.template", "base.vm");

But this didn't resolve the issue. Am I adding layout template location incorrectly? Btw, this is a Spring Boot application.

Should I switch to using macros?

UPD: here are my dependencies from pom.xml:

        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity.tools</groupId>
            <artifactId>velocity-tools-generic</artifactId>
            <version>3.1</version>
        </dependency>

Solution

  • The $screen_content and $layout feature is provided by the VelocityLayoutServlet, in a web context (and from the velocity-tools-view package).

    In your case, you must do it manually. The layout configuration values you give to velocity will be ignored since the VelocityLayoutServlet is not invoked.

    If you have a single layout

    It's much easier since you can hardcode the layout path.

            VelocityEngine velocityEngine = new VelocityEngine();
            velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "classpath");
            velocityEngine.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
            velocityEngine.init();
    
            Template t = velocityEngine.getTemplate("/mail-templates/base.vm");
    
            VelocityContext context = new VelocityContext();
            context.put("user", "testing");
            context.put("page", "mail-templates/my_template.vm");
    
            StringWriter writer = new StringWriter();
            t.merge(context, writer);
    

    And then, inside base.vm:

    <body style="font-family:'Proxima Nova',Arial,sans-serif; background-color: #f4f4f4; margin: 0; padding: 30px 0;">
    
    <table style="margin: 0 auto; background-color: #ffffff; border-spacing: 0; min-width: 490px;">
        <tbody style="font-family: 'Proxima Nova',Arial,sans-serif; font-size: 16px; color: #303744;">
            #parse($page)
            #parse("/snippets/footer_content_separator.vm")
            #parse("/snippets/footer_content.vm")
        </tbody>
    </table>
    
    </body>
    

    If each page should decide its layout

    It's a bit more complex. You would typically merge the content page in a string writer, then look inside the context which layout has been chosen by the content page, if any, then merge the layout page, putting the result of the content page merge in a $screen_content variable.