Re: Using Velocity templates as applied decorator panel content

Scott Farquhar <[email protected]>
Newsgroups gmane.comp.web.sitemesh.general
Message-ID <[email protected]>
This needs a whole lot of cleaning up, but is what we use in Confluence.

Please see the attachment.

Cheers,
Scott

On Wed, Mar 30, 2005 at 07:05:16AM +0100, Joe Walnes wrote:
> Hi Wendel,
> 
> Out of the box, there is currently no support for Velocity decorators. 
> 
> However, if you really need it, it shouldn't be too hard to implement
> yourself (and even contribute back) :). Basically, you need to could
> write a macro that loads cart.vm into an String, passes the string to
> HtmlPageParser (a SiteMesh class), which converts it to a Page object
> (another SiteMesh class), then include your 'panel.vm' decorator,
> passing the Page instance into it.
> 
> cheers
> -Joe
> 
> 
> 
> 
> 
> On Tue, 29 Mar 2005 18:10:50 CST, Wendel Schultz
> <[email protected]> wrote:
> > I'm one week old in Struts, Velocity and Sitemesh.  Please be gentle.
> > 
> > I'm coming along ok I suppose.  I can get struts actions to find a velocity template.  I can use that template to generate some content.  I can use Sitemesh to decorate it.  I can do all the "hello world" apps in each of the respective tools.  I'm trying to integrate some of them and get a feel for how these tools work together.
> > 
> > What I think I want to do is use Sitemesh to handle page composition/decoration.  I see that there is a lot of activity recently about Sitemesh and composition enhancements.  I'd love to see them.  I want to use Velocity to create content to apply my decorators to. Example:
> > 
> > <page:applyDecorator name="main" page="/vm/cart.vm"/>
> > 
> > I can't seem to use a velocity template as content for a decorator to decorate.
> > 
> > I have found Velocity decorators, but
> > 
> > #parse( "/vm/cart.vm" )
> > 
> > is in no way the same.  It seems to represent the same problems the old includes provided.
> > 
> > Maybe I am missing a fairly large neon sign.  Any suggestions?
> > 
> > ---------------------------------------------------------------------
> > Posted via Jive Forums
> > http://forums.opensymphony.com/thread.jspa?threadID=1139&messageID=3645#3645
> > 
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: [email protected]
> > For additional commands, e-mail: [email protected]
> > 
> >
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
ApplyDecoratorDirective.java (text/plain, 8.7 KB)
/*
 * Created by IntelliJ IDEA.
 * User: Mike
 * Date: May 20, 2004
 * Time: 2:47:50 PM
 */
package com.atlassian.confluence.setup.velocity;

import com.atlassian.confluence.util.VelocityUtils;
import com.atlassian.confluence.util.profiling.ProfilingPageFilter;
import com.atlassian.confluence.setup.webwork.ConfluenceVelocityContext;
import com.atlassian.confluence.setup.sitemesh.ConfluenceSpaceDecoratorMapper;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.spaces.SpaceManager;
import com.atlassian.util.profiling.UtilTimerStack;
import com.atlassian.seraph.config.SecurityConfigFactory;
import com.opensymphony.module.sitemesh.*;
import com.opensymphony.module.sitemesh.parser.FastPageParser;
import com.opensymphony.module.sitemesh.util.OutputConverter;
import com.opensymphony.webwork.views.velocity.VelocityManager;
import com.opensymphony.xwork.ActionContext;
import org.apache.log4j.Category;
import org.apache.velocity.context.Context;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.directive.Directive;
import org.apache.velocity.runtime.parser.node.Node;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import bucket.container.ContainerManager;
import bucket.util.FileUtils;

/**
 * An ApplyDecorator directive for Sitemesh.
 *
 * Example use:
 * <p>
 * #applyDecorator("mydecorator" "inline title")<br>
 *  &nbsp; The body goes here.
 * #end<br>
 * <p>
 * The title attribute is optional.
 * <p>
 * @see ParamDirective
 */
public final class ApplyDecoratorDirective extends Directive
{
    private static final Category log = Category.getInstance(ApplyDecoratorDirective.class);
    public static final String STACK_KEY = ApplyDecoratorDirective.DirectiveStack.class.getName();
    private DirectiveStack stack;
    private Map params = new HashMap();

    /**
     * Returns the name of the directive.
     *
     * @return name of the directive
     */
    public String getName()
    {
        return "applyDecorator";
    }

    /**
     * Tells velocity that this is a block-type directive.
     *
     * @return directive type
     */
    public int getType()
    {
        return BLOCK;
    }

    /**
     * The directive is initialized.
     *
     * @param services Velocity runtime services.
     * @param adapter  context.
     * @param node     within the directive.
     */
    public void init(RuntimeServices services, InternalContextAdapter adapter, Node node) throws Exception
    {
        super.init(services, adapter, node);

        int numArgs = node.jjtGetNumChildren();

        if (numArgs < 2)
        {
            services.error("#applyDecorator error: You need a decorator name in order to use this tag");
        }
        else if (numArgs > 3)
        {
            services.error("#applyDecorator error: Too many parameters");
        }
    }

    /**
     * Does the actual decoration.
     *
     * @param adapter the context.
     * @param writer  a writer instance to which to write the decorated text.
     * @param node    the node within our block.
     */
    public boolean render(InternalContextAdapter adapter, Writer writer, Node node)
            throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException
    {
        UtilTimerStack.push("ApplyDecoratorDirective.render()");

        stack = (DirectiveStack) adapter.get(STACK_KEY);

        if (stack == null)
        {
            stack = new DirectiveStack();
            adapter.put(STACK_KEY, stack);
        }

        stack.push(this);

        try
        {
            HttpServletRequest request = (HttpServletRequest) adapter.get(VelocityManager.REQUEST);
            if (request == null)
                throw new IOException("No request object in context.");

            HttpServletResponse response = (HttpServletResponse) adapter.get(VelocityManager.RESPONSE);
            if (response == null)
                throw new IOException("No response object in context.");

            String decoratorName = (String) node.jjtGetChild(0).value(adapter);
            StringWriter bodyContent = new StringWriter(1024);

            int bodyNode = 1;

            if (node.jjtGetNumChildren() == 3)
                bodyNode = 2;

            node.jjtGetChild(bodyNode).render(adapter, bodyContent);

            Factory factory = ProfilingPageFilter.getFactory();
            PageParser parser = factory.getPageParser("text/html");
            HTMLPage page = (HTMLPage) ((FastPageParser) parser).parse(new StringReader(bodyContent.toString()));

            /**
             * It's a necessary ugliness to hide the page as an attribute in the request as below:
             * much of our logic for providing space specific themes relies upon having a page object present.
             *
             * So, when we handle an inlined decorator we need to check a theme plugin for a decorator which
             * overrides it. The page object is needed by the corresponding mapper (ConfluenceSpaceDecoratorMapper)
             * when it looks for a named decorator (getNamedDecorator(..)) so we hide it in the request.
             *
             * I think this problem is emerging because we're asking quite a bit of flexibility from the
             * code which fetches a decorator. It's an elegant idea but it doesn't seem to be supported by the sitemesh
             * API.
             */
            request.setAttribute("sitemeshPage", page);
            Decorator decorator = factory.getDecoratorMapper().getNamedDecorator(request, decoratorName);
            request.removeAttribute("sitemeshPage");

            if (decorator != null)
            {
                Context context = VelocityManager.getInstance().createContext(ActionContext.getContext().getValueStack(), request, response);

                //sets up the current velocity context for the current request (user and userhistory var.s are introduced)
                ConfluenceVelocityContext.modifyRequestContext(context, request);

                context.put("sitemeshPage", page);

                // get title from #applyDecorator directive if they specified it, otherwise use <title> in content
                if (node.jjtGetNumChildren() == 3)
                    context.put("title", (String) node.jjtGetChild(1).value(adapter));
                else
                    context.put("title", page.getTitle());

                {
                    StringWriter buffer = new StringWriter();
                    page.writeBody(OutputConverter.getWriter(buffer));
                    context.put("body", buffer.toString());
                }
                {
                    StringWriter buffer = new StringWriter();
                    page.writeHead(OutputConverter.getWriter(buffer));
                    context.put("head", buffer.toString());
                }

                context.put("params", params);

                writer.write(VelocityUtils.getRenderedTemplate(decorator.getPage(), context));
            }
            else
            {
                throw new IOException("could not find decorator with name: " + decoratorName);
            }

            return true;
        }
        finally
        {
            stack.pop();
            UtilTimerStack.pop("ApplyDecoratorDirective.render()");
        }
    }

    public void addParameter(String paramName, Object paramValue)
    {
        params.put(paramName, paramValue);
    }

    public class DirectiveStack
    {
        Stack stack;

        public DirectiveStack()
        {
            this.stack = new Stack();
        }

        public ApplyDecoratorDirective pop()
        {
            try
            {
                return (ApplyDecoratorDirective)stack.pop();
            }
            catch (EmptyStackException e)
            {
                log.info("Someone's been popping out of order! " + e.getMessage(), e);
            }
            return null;
        }

        public void push(ApplyDecoratorDirective directive)
        {
            stack.push(directive);
        }

        public ApplyDecoratorDirective peek()
        {
            if (stack.size() > 0)
                return (ApplyDecoratorDirective) stack.peek();
            else
                return null;
        }
    }

}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.