diff --git a/config/alfresco/web-pagerenderer-application-context.xml b/config/alfresco/web-pagerenderer-application-context.xml
new file mode 100644
index 0000000000..e23734930e
--- /dev/null
+++ b/config/alfresco/web-pagerenderer-application-context.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/web-pagerenderer-config.xml b/config/alfresco/web-pagerenderer-config.xml
new file mode 100644
index 0000000000..92d9cf3a93
--- /dev/null
+++ b/config/alfresco/web-pagerenderer-config.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java b/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java
new file mode 100644
index 0000000000..659c2d41ac
--- /dev/null
+++ b/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2005-2007 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing
+ */
+package org.alfresco.web.app.servlet;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.alfresco.config.Config;
+import org.alfresco.config.ConfigService;
+import org.alfresco.config.JNDIConstants;
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.avm.AVMNodeConverter;
+import org.alfresco.repo.template.ClassPathRepoTemplateLoader;
+import org.alfresco.service.cmr.avm.AVMNotFoundException;
+import org.alfresco.service.cmr.avm.AVMService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.web.scripts.WebScriptCache;
+import org.alfresco.web.scripts.WebScriptMatch;
+import org.alfresco.web.scripts.WebScriptRequest;
+import org.alfresco.web.scripts.WebScriptResponse;
+import org.alfresco.web.scripts.WebScriptRuntime;
+import org.alfresco.web.scripts.WebScriptServlet;
+import org.alfresco.web.scripts.WebScriptURLRequest;
+import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+import freemarker.cache.MultiTemplateLoader;
+import freemarker.cache.TemplateLoader;
+
+/**
+ * Servlet for rendering templated pages based on Webscript components.
+ *
+ * NOTE: No web-client helper classes should be used here! This servlet should be movable
+ * to the headerless repo remote client with the minimum of work.
+ *
+ * @author Kevin Roast
+ */
+public class PageRendererServlet extends WebScriptServlet
+{
+ private static Log logger = LogFactory.getLog(PageRendererServlet.class);
+
+ private static final String MIMETYPE_HTML = "text/html;charset=utf-8";
+
+ private PageTemplateProcessor templateProcessor;
+ private WebScriptTemplateLoader webscriptTemplateLoader;
+ private Map defaultPageDefCache = null;
+
+ @Override
+ public void init() throws ServletException
+ {
+ super.init();
+
+ // init beans
+ ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
+ templateProcessor = (PageTemplateProcessor)context.getBean("pagerenderer.templateprocessor");
+ webscriptTemplateLoader = new WebScriptTemplateLoader();
+ ClassPathRepoTemplateLoader repoLoader = new ClassPathRepoTemplateLoader(
+ serviceRegistry.getNodeService(),
+ serviceRegistry.getContentService(),
+ templateProcessor.getDefaultEncoding());
+ templateProcessor.setTemplateLoader(new MultiTemplateLoader(new TemplateLoader[]{
+ webscriptTemplateLoader, repoLoader}));
+ templateProcessor.initConfig();
+
+ // we use a specific config service instance
+ configService = (ConfigService)context.getBean("pagerenderer.config");
+
+ // create cache for default config
+ defaultPageDefCache = Collections.synchronizedMap(new HashMap());
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException
+ {
+ String uri = req.getRequestURI();
+
+ if (logger.isDebugEnabled())
+ {
+ String qs = req.getQueryString();
+ logger.debug("Processing Page Renderer URL: (" + req.getMethod() + ") " + uri +
+ ((qs != null && qs.length() != 0) ? ("?" + qs) : ""));
+ }
+
+ uri = uri.substring(req.getContextPath().length()); // skip server context path
+ StringTokenizer t = new StringTokenizer(uri, "/");
+ int tokenCount = t.countTokens();
+ t.nextToken(); // skip servlet name
+ if (t.hasMoreTokens() == false)
+ {
+ throw new IllegalArgumentException("Invalid URL to PageRendererServlet: " + uri);
+ }
+
+ // retrieve the page name from the url
+ String page = t.nextToken();
+
+ // the AVM store to retrieve pages and config from
+ // TODO: find a better way to set the store for a website servlet
+ String store = req.getParameter("store");
+ if (store == null)
+ {
+ throw new AlfrescoRuntimeException("'store' argument mandatory for PageRendererServlet.");
+ }
+
+ try
+ {
+ // lookup template path from page config in website AVM store
+ PageDefinition pageDefinition = lookupPageDefinition(store, page);
+
+ // set response content type and charset
+ res.setContentType(MIMETYPE_HTML);
+
+ // TODO: What authentication to use here? Guest? Or all templates and webscripts
+ // must reside in a known AVM repo - i.e. skip permissions. Template API still
+ // uses NodeService for some calls - even for AVMTemplateNode - so will need
+ // some kind of security context...
+ authenticate(getServletContext(), req, res);
+
+ // set the web app context path for the template loader to use when rebuilding urls
+ this.webscriptTemplateLoader.setContextPath(req.getContextPath());
+ this.webscriptTemplateLoader.setPageDefinition(pageDefinition);
+
+ // Process the template page using our custom loader - the loader will find and buffer
+ // individual included webscript output into the main writer for the servlet page.
+ // TODO: where does the theme name come from!? same problem as where does store name come from...
+ String templatePath = getStoreSitePath(store) + '/' + "themes" + '/' + "default" + '/' +
+ "templates" + '/' + pageDefinition.TemplateName;
+
+ if (logger.isDebugEnabled())
+ logger.debug("Page template resolved as: " + templatePath);
+
+ processTemplatePage(templatePath, req, res);
+ }
+ catch (Throwable err)
+ {
+ throw new AlfrescoRuntimeException("Error occurred during page rendering. Page id: " +
+ page + " with error: " + err.getMessage(), err);
+ }
+ }
+
+ private void processTemplatePage(String templatePath, HttpServletRequest req, HttpServletResponse res)
+ throws IOException
+ {
+ NodeRef ref = AVMNodeConverter.ToNodeRef(-1, templatePath);
+ templateProcessor.process(ref.toString(), getModel(req), res.getWriter());
+ }
+
+ private Object getModel(HttpServletRequest req)
+ {
+ // TODO: add the full template model here?
+ // just a basic minimum model for now
+ Map model = new HashMap();
+ model.put("url", new URLHelper(req.getContextPath()));
+ return model;
+ }
+
+ /**
+ * Get the page definition config object based on the page ID and AVM store.
+ *
+ * @param store AVM store to retrieve data from
+ * @param page Page ID to lookup in web-pagerenderer-config.xml
+ *
+ * @return Path to the template content
+ */
+ private PageDefinition lookupPageDefinition(String store, String page)
+ {
+ // Lookup page via standard Alfresco XML config
+ /*Config config = getConfig();
+ if (config != null)
+ {
+ ConfigElement pagesConfig = config.getConfigElement("pages");
+ for (ConfigElement pageConfig : pagesConfig.getChildren("page"))
+ {
+ String pageId = pageConfig.getAttribute("id");
+ if (page.equals(pageId))
+ {
+ // found page reference
+ if (logger.isDebugEnabled())
+ logger.debug("Looked up page id: " + page + " as " + pageConfig.getValue());
+ pageValue = pageConfig.getValue();
+ break;
+ }
+ }
+ }*/
+
+ // Lookup (and cache) config for default page-definition file in root
+ PageDefinition defaultPageDef = defaultPageDefCache.get(store);
+ if (defaultPageDef == null)
+ {
+ // read default config for the site and cache the result
+ String defaultConfigPath = getStoreSitePath(store) + '/' + "pages" + '/' + "page-definition.xml";
+ defaultPageDef = readPageDefinitionConfig(defaultConfigPath, page, null);
+ defaultPageDefCache.put(store, defaultPageDef);
+ }
+
+ // Lookup page xml config in AVM store using page name as location
+ String configPath = getStoreSitePath(store) + '/' + "pages" + '/' + page + '/' + "page-definition.xml";
+
+ // read the page definition and return it
+ return readPageDefinitionConfig(configPath, page, defaultPageDef);
+ }
+
+ /**
+ * Read the page definition config at the specified location. A default config object can be
+ * supplied from which values will be taken if none are supplied in the config that is read.
+ *
+ * @return PageDefinition representing the config
+ */
+ private PageDefinition readPageDefinitionConfig(String configPath, String page, PageDefinition defaultPageDef)
+ {
+ PageDefinition pageDef = null;
+
+ AVMService avm = this.serviceRegistry.getAVMService();
+ try
+ {
+ // parse page definition xml config file
+ // TODO: convert to pull parser to optimize (see importer ViewParser)
+ SAXReader reader = new SAXReader();
+ try
+ {
+ Document document = reader.read(avm.getFileInputStream(-1, configPath));
+
+ Element rootElement = document.getRootElement();
+ if (!rootElement.getName().equals("page"))
+ {
+ throw new AlfrescoRuntimeException(
+ "Expected 'page' root element in page-definition.xml config: " + configPath);
+ }
+
+ String templateName = null;
+ if (defaultPageDef != null && defaultPageDef.TemplateName != null)
+ {
+ // take template name from default config in case none is specified locally
+ templateName = defaultPageDef.TemplateName;
+ }
+ Element templateElement = rootElement.element("template");
+ if (defaultPageDef != null && (templateElement == null && templateName == null))
+ {
+ throw new AlfrescoRuntimeException(
+ "No 'template' element (and no default set) found in page-definition.xml config: " + configPath);
+ }
+ templateName = templateElement.attributeValue("name");
+ if (templateName == null || templateName.length() == 0)
+ {
+ throw new AlfrescoRuntimeException(
+ "The 'template' element is missing mandatory 'name' attribute in page-definition.xml config: " +
+ configPath);
+ }
+
+ // create config object for this page and store template name for this page
+ pageDef = new PageDefinition(templateName);
+
+ // copy in component mappings from default config definitions first
+ if (defaultPageDef != null)
+ {
+ pageDef.Components.putAll(defaultPageDef.Components);
+ }
+
+ // read the component defs for this page
+ // removing 'disabled' components as configured locally
+ Element componentsElements = rootElement.element("components");
+ if (componentsElements != null)
+ {
+ for (Element ce : (List)componentsElements.elements("component"))
+ {
+ // read the mandatory component 'id' attribute
+ String id = ce.attributeValue("id");
+ if (id == null || id.length() == 0)
+ {
+ throw new AlfrescoRuntimeException(
+ "A 'component' element is missing mandatory 'id' attribute in page-definition.xml config: " +
+ configPath);
+ }
+
+ // next check for the 'disabled' boolean attribute - used to disable components
+ // that are specified in the default config - we don't need to read further
+ String disabled = ce.attributeValue("disabled");
+ if (disabled != null)
+ {
+ if (Boolean.parseBoolean(disabled) == true)
+ {
+ pageDef.Components.remove(id);
+ continue;
+ }
+ }
+
+ String url = ce.attributeValue("url");
+ if (url == null || url.length() == 0)
+ {
+ throw new AlfrescoRuntimeException(
+ "A 'component' element is missing mandatory 'url' attribute in page-definition.xml config: " +
+ configPath);
+ }
+
+ // create minimum component config definition
+ PageComponent component = new PageComponent(id, url);
+
+ // store any other component properties
+ for (Element pe : (List)ce.elements())
+ {
+ component.Properties.put(pe.getName(), pe.getTextTrim());
+ }
+
+ // store component definition in the page definition
+ pageDef.Components.put(component.Id, component);
+ }
+ }
+ }
+ catch (DocumentException docErr)
+ {
+ throw new AlfrescoRuntimeException("Failed to parse 'page-definition.xml' for page '" + page +
+ "' in config: " + configPath, docErr);
+ }
+ }
+ catch (AVMNotFoundException avmErr)
+ {
+ throw new AlfrescoRuntimeException("Unable to find 'page-definition.xml' for page '" + page +
+ "' in expected location path: " + configPath, avmErr);
+ }
+
+ return pageDef;
+ }
+
+ private Config getConfig()
+ {
+ return this.configService.getConfig("PageRenderer");
+ }
+
+ private static void authenticate(ServletContext sc, HttpServletRequest req, HttpServletResponse res)
+ throws IOException
+ {
+ WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
+ AuthenticationService auth = (AuthenticationService)wc.getBean("AuthenticationService");
+ auth.authenticate("admin", "admin".toCharArray());
+ //auth.authenticateAsGuest();
+ }
+
+ private static String getStoreSitePath(String store)
+ {
+ return store + ":/" + JNDIConstants.DIR_DEFAULT_WWW + '/' + "avm_site";
+ }
+
+
+ /**
+ * WebScript runtime for the PageRenderer servlet.
+ */
+ private class PageRendererWebScriptRuntime extends WebScriptRuntime
+ {
+ private String webScript;
+ private String scriptUrl;
+ private String encoding;
+ private ByteArrayOutputStream baOut = null;
+
+ PageRendererWebScriptRuntime(String webScript, String scriptUrl, String encoding)
+ {
+ super(registry, serviceRegistry);
+ this.encoding = encoding;
+ this.webScript = webScript;
+ this.scriptUrl = scriptUrl;
+ if (logger.isDebugEnabled())
+ logger.debug("Constructing runtime for url: " + scriptUrl);
+ }
+
+ @Override
+ protected String getScriptUrl()
+ {
+ return webScript;
+ }
+
+ @Override
+ protected WebScriptRequest createRequest(WebScriptMatch match)
+ {
+ return new WebScriptPageRendererRequest(scriptUrl, match);
+ }
+
+ @Override
+ protected WebScriptResponse createResponse()
+ {
+ // create a response object that we control to write to a temporary output
+ // we later use that as the source for the webscript "template"
+ try
+ {
+ this.baOut = new ByteArrayOutputStream(4096);
+ BufferedWriter wrOut = new BufferedWriter(
+ encoding == null ? new OutputStreamWriter(baOut) : new OutputStreamWriter(baOut, encoding));
+ return new WebScriptPageRendererResponse(wrOut, baOut);
+ }
+ catch (UnsupportedEncodingException err)
+ {
+ throw new AlfrescoRuntimeException("Unsupported encoding.", err);
+ }
+ }
+
+ @Override
+ protected boolean authenticate(RequiredAuthentication required, boolean isGuest)
+ {
+ // TODO: what authentication here?
+ return true;
+ }
+
+ @Override
+ protected String getScriptMethod()
+ {
+ return "GET";
+ }
+
+ public Reader getResponseReader()
+ {
+ try
+ {
+ if (baOut == null)
+ {
+ return null;
+ }
+ else
+ {
+ return new BufferedReader(new InputStreamReader(
+ encoding == null ? new ByteArrayInputStream(baOut.toByteArray()) :
+ new ByteArrayInputStream(baOut.toByteArray()), encoding));
+ }
+ }
+ catch (UnsupportedEncodingException err)
+ {
+ throw new AlfrescoRuntimeException("Unsupported encoding.", err);
+ }
+ }
+ }
+
+ /**
+ * Simple implementation of a WebScript URL Request for a webscript on the page
+ */
+ private class WebScriptPageRendererRequest extends WebScriptURLRequest
+ {
+ WebScriptPageRendererRequest(String scriptUrl, WebScriptMatch match)
+ {
+ super(scriptUrl, match);
+ }
+
+ public String getAgent()
+ {
+ return null;
+ }
+
+ public String getServerPath()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Implementation of a WebScript Response object for PageRenderer servlet
+ */
+ private class WebScriptPageRendererResponse implements WebScriptResponse
+ {
+ private Writer outWriter;
+ private OutputStream outStream;
+
+ public WebScriptPageRendererResponse(Writer outWriter, OutputStream outStream)
+ {
+ this.outWriter = outWriter;
+ this.outStream = outStream;
+ }
+
+ public String encodeScriptUrl(String url)
+ {
+ // TODO: some kind of encoding required here - need to allow webscripts to call themselves
+ // on this page - so need the servlet PageRenderer URL plus args to identify the webscript
+ // and it's new url - similar to the JSF or Portlet runtimes
+ return url;
+ }
+
+ public String getEncodeScriptUrlFunction(String name)
+ {
+ // TODO: may be required?
+ return null;
+ }
+
+ public OutputStream getOutputStream() throws IOException
+ {
+ return this.outStream;
+ }
+
+ public Writer getWriter() throws IOException
+ {
+ return this.outWriter;
+ }
+
+ public void reset()
+ {
+ // not supported
+ }
+
+ public void setCache(WebScriptCache cache)
+ {
+ // not supported
+ }
+
+ public void setContentType(String contentType)
+ {
+ // not supported
+ }
+
+ public void setStatus(int status)
+ {
+ // not supported
+ }
+ }
+
+ /**
+ * Template loader that resolves and executes webscript components by looking up layout keys
+ * in the template against the component definition service URLs for the page.
+ */
+ private class WebScriptTemplateLoader implements TemplateLoader
+ {
+ private ThreadLocal pageDefinition = new ThreadLocal();
+ private String contextPath;
+ private long last = 0L;
+
+ public void closeTemplateSource(Object templateSource) throws IOException
+ {
+ // nothing to do
+ }
+
+ public Object findTemplateSource(String name) throws IOException
+ {
+ // The webscript is looked up based on the key in the #include directive - it must
+ // be of the form [somekey] so that it can be recognised by the loader
+
+ // most templates included by this loader will be children of other templates
+ // unfortunately FreeMarker attempts to build paths for you to child templates - they are not
+ // really children - so this information must be discarded
+ if (name.startsWith("avm://"))
+ {
+ name = name.substring(name.indexOf("/[") + 1);
+ }
+
+ if (name.startsWith("[") && name.endsWith("]"))
+ {
+ String key = name.substring(1, name.length() - 1);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Found webscript component key: " + key);
+
+ // lookup against component def config
+ PageComponent component = this.pageDefinition.get().Components.get(key);
+ if (component == null)
+ {
+ // TODO: if the lookup fails, exception or just ignore render and log...?
+ throw new AlfrescoRuntimeException("Failed to find component identified by key '" + key +
+ "' found in template: " + pageDefinition.get().TemplateName);
+ }
+
+ // TODO: remove the /service prefix from all config files?
+ String url = component.Url;
+ if (url.lastIndexOf('?') != -1)
+ {
+ url = url.substring("/service".length(), url.lastIndexOf('?'));
+ }
+ else
+ {
+ url = url.substring("/service".length());
+ }
+ return url;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public long getLastModified(Object templateSource)
+ {
+ return last--;
+ }
+
+ public Reader getReader(Object templateSource, String encoding) throws IOException
+ {
+ // Execute the webscript and return a Reader to the textual content
+ String webscriptUrl = this.contextPath + "/service" + templateSource.toString();
+ PageRendererWebScriptRuntime runtime = new PageRendererWebScriptRuntime(
+ templateSource.toString(), webscriptUrl, encoding);
+ runtime.executeScript();
+
+ // Return a reader from the runtime that executed the webscript - this effectively
+ // returns the result as a "template" source to freemarker. Generally this will not itself
+ // be a template but it can contain additional freemarker syntax if required. The downside
+ // to this approach is that the result of the webscript is parsed again by freemarker - this
+ // should be checked for performance issues.
+ return runtime.getResponseReader();
+ }
+
+ /**
+ * Setter to apply the current page definition for this template execution. A ThreadLocal is used
+ * to allow multiple servlet threads to execute using the same TemplateLoader (there can only be one)
+ * but with different page definitions for each thread.
+ */
+ public void setPageDefinition(PageDefinition pageDef)
+ {
+ this.pageDefinition.set(pageDef);
+ }
+
+ /**
+ * Setter called by the servlet to ensure the loader has the full context path available
+ * Does not matter if called multiple times by multiple threads as value is always the same.
+ */
+ public void setContextPath(String path)
+ {
+ this.contextPath = path;
+ }
+ }
+
+ /**
+ * Helper to return context path for generating urls
+ */
+ public static class URLHelper
+ {
+ String context;
+
+ public URLHelper(String context)
+ {
+ this.context = context;
+ }
+
+ public String getContext()
+ {
+ return context;
+ }
+ }
+
+ private static class PageDefinition
+ {
+ public String TemplateName;
+ public Map Components = new HashMap();
+
+ PageDefinition(String templateId)
+ {
+ this.TemplateName = templateId;
+ }
+ }
+
+ private static class PageComponent
+ {
+ public String Id;
+ public String Url;
+ public Map Properties = new HashMap(4, 1.0f);
+
+ PageComponent(String id, String url)
+ {
+ this.Id = id;
+ this.Url = url;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java b/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java
new file mode 100644
index 0000000000..33f133e5c8
--- /dev/null
+++ b/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2007 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing
+ */
+package org.alfresco.web.app.servlet;
+
+import org.alfresco.web.scripts.TemplateProcessor;
+
+/**
+ * Extension to the webscripts TemplateProcessor class to allow the default encoding
+ * to be returned and also enable config to be initialised from a public accessor.
+ *
+ * @author Kevin Roast
+ */
+public class PageTemplateProcessor extends TemplateProcessor
+{
+ public String getDefaultEncoding()
+ {
+ return this.defaultEncoding;
+ }
+
+ /**
+ * Initialise FreeMarker Configuration
+ */
+ public void initConfig()
+ {
+ super.initConfig();
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/web/scripts/TemplateProcessor.java b/source/java/org/alfresco/web/scripts/TemplateProcessor.java
index 96d9d771ef..3eca3d8685 100644
--- a/source/java/org/alfresco/web/scripts/TemplateProcessor.java
+++ b/source/java/org/alfresco/web/scripts/TemplateProcessor.java
@@ -56,10 +56,10 @@ import freemarker.template.TemplateExceptionHandler;
public class TemplateProcessor extends FreeMarkerProcessor implements ApplicationContextAware, ApplicationListener
{
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
- private TemplateLoader templateLoader = null;
- private String defaultEncoding;
- private Configuration templateConfig;
- private FreeMarkerProcessor freeMarkerProcessor;
+ protected TemplateLoader templateLoader = null;
+ protected String defaultEncoding;
+ protected Configuration templateConfig;
+ protected FreeMarkerProcessor freeMarkerProcessor;
/* (non-Javadoc)
@@ -154,6 +154,9 @@ public class TemplateProcessor extends FreeMarkerProcessor implements Applicatio
// rethrow any exception so we can deal with them
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+ // turn off locale sensitive lookup - to save numerous wasted calls to nodeservice.exists()
+ config.setLocalizedLookup(false);
+
// set template encoding
if (defaultEncoding != null)
{
diff --git a/source/java/org/alfresco/web/scripts/WebScriptServlet.java b/source/java/org/alfresco/web/scripts/WebScriptServlet.java
index 56694e9b07..45413436de 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptServlet.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptServlet.java
@@ -54,13 +54,13 @@ public class WebScriptServlet extends HttpServlet
private static final Log logger = LogFactory.getLog(WebScriptServlet.class);
// Component Dependencies
- private DeclarativeWebScriptRegistry registry;
- private ServiceRegistry serviceRegistry;
- private WebScriptServletAuthenticator authenticator;
+ protected DeclarativeWebScriptRegistry registry;
+ protected ServiceRegistry serviceRegistry;
+ protected WebScriptServletAuthenticator authenticator;
protected ConfigService configService;
/** Host Server Configuration */
- private static ServerConfigElement serverConfig;
+ protected static ServerConfigElement serverConfig;
@Override
diff --git a/source/web/WEB-INF/web.xml b/source/web/WEB-INF/web.xml
index d82ab30e56..1321bb398c 100644
--- a/source/web/WEB-INF/web.xml
+++ b/source/web/WEB-INF/web.xml
@@ -71,6 +71,7 @@
classpath:alfresco/application-context.xml
classpath:alfresco/web-client-application-context.xml
classpath:alfresco/web-scripts-application-context.xml
+ classpath:alfresco/web-pagerenderer-application-context.xml
classpath:web-services-application-context.xml
Spring config file locations
@@ -290,6 +291,11 @@
JBPMDeployProcessServlet
org.alfresco.web.app.servlet.JBPMDeployProcessServlet
+
+
+ pageRendererServlet
+ org.alfresco.web.app.servlet.PageRendererServlet
+
Faces Servlet
@@ -430,6 +436,11 @@
workflowDefinitionImageServlet
/workflowdefinitionimage/*
+
+
+ pageRendererServlet
+ /page/*
+
60