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>
* 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;
}
}
}