Implement multi-location template loading to look for a template first in the themes directory, then in the vitro template directory.

This commit is contained in:
rjy7 2010-05-12 15:08:56 +00:00
parent a0c93fda6d
commit 7c72f0f37c
4 changed files with 55 additions and 29 deletions

View file

@ -2,6 +2,7 @@
package edu.cornell.mannlib.vitro.webapp.controller; package edu.cornell.mannlib.vitro.webapp.controller;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
@ -13,6 +14,7 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -27,6 +29,11 @@ import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils; import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
import edu.cornell.mannlib.vitro.webapp.view.menu.TabMenu; import edu.cornell.mannlib.vitro.webapp.view.menu.TabMenu;
import edu.cornell.mannlib.vitro.webapp.web.PortalWebUtil; import edu.cornell.mannlib.vitro.webapp.web.PortalWebUtil;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
@ -34,11 +41,12 @@ import freemarker.template.TemplateModelException;
public class FreeMarkerHttpServlet extends VitroHttpServlet { public class FreeMarkerHttpServlet extends VitroHttpServlet {
private static final Log log = LogFactory.getLog(FreeMarkerHttpServlet.class.getName()); private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(FreeMarkerHttpServlet.class.getName());
private static final int FILTER_SECURITY_LEVEL = LoginFormBean.EDITOR; private static final int FILTER_SECURITY_LEVEL = LoginFormBean.EDITOR;
public static Configuration config = null; public static Configuration config = null;
public static String contextPath = null; // RY or do we need to store the entire ServletContext? public static String contextPath = null;
protected VitroRequest vreq; protected VitroRequest vreq;
protected HttpServletResponse response; protected HttpServletResponse response;
@ -50,6 +58,7 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
// a getBody() and getTitle() method and use the parent doGet() method. // a getBody() and getTitle() method and use the parent doGet() method.
public void doGet( HttpServletRequest request, HttpServletResponse response ) public void doGet( HttpServletRequest request, HttpServletResponse response )
throws IOException, ServletException { throws IOException, ServletException {
try { try {
doSetup(request, response); doSetup(request, response);
setTitle(); setTitle();
@ -162,12 +171,12 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
try { try {
super.doGet(request,response); super.doGet(request,response);
} catch (ServletException e1) { } catch (ServletException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e1.printStackTrace(); e.printStackTrace();
} catch (IOException e1) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e1.printStackTrace(); e.printStackTrace();
} }
vreq = new VitroRequest(request); vreq = new VitroRequest(request);
@ -183,6 +192,8 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
log.error("Can't set shared variable 'portalId'."); log.error("Can't set shared variable 'portalId'.");
} }
setTemplateLoader();
TabMenu menu = getTabMenu(portalId); TabMenu menu = getTabMenu(portalId);
root.put("tabMenu", menu); root.put("tabMenu", menu);
@ -226,7 +237,6 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
} }
urls.put("about", getUrl(Controllers.ABOUT + "?home=" + portalId)); urls.put("about", getUrl(Controllers.ABOUT + "?home=" + portalId));
urls.put("aboutFM", getUrl(Controllers.ABOUT + "-fm?home=" + portalId)); // TEMPORARY
if (ContactMailServlet.getSmtpHostFromProperties() != null) { if (ContactMailServlet.getSmtpHostFromProperties() != null) {
urls.put("contact", getUrl(Controllers.CONTACT_URL + "?home=" + portalId)); urls.put("contact", getUrl(Controllers.CONTACT_URL + "?home=" + portalId));
} }
@ -278,6 +288,32 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
} }
} }
// Define template locations. Template loader will look first in the theme-specific
// location, then in the vitro location.
// RY We cannot do this in FreeMarkerSetup because (a) the theme depends on the portal,
// and we have multi-portal installations, and (b) we need to support theme-switching on the fly.
// To make more efficient, we could do this once, and then have a listener that does it again
// when theme is switched.BUT this doesn't support (a), only (b), so we have to do it on every request.
private final void setTemplateLoader() {
// RY If this is needed in other methods, put in instance var
ServletContext context = getServletContext();
String themeTemplateDir = context.getRealPath(portal.getThemeDir()) + "/ftl";
String vitroTemplateDir = context.getRealPath("/templates/freemarker");
try {
FileTemplateLoader themeFtl = new FileTemplateLoader(new File(themeTemplateDir));
FileTemplateLoader vitroFtl = new FileTemplateLoader(new File(vitroTemplateDir));
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "");
TemplateLoader[] loaders = new TemplateLoader[] { themeFtl, vitroFtl, ctl };
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);
config.setTemplateLoader(mtl);
} catch (IOException e) {
log.error("Error loading templates");
}
}
public static String getUrl(String path) { public static String getUrl(String path) {
if ( ! path.startsWith("/") ) { if ( ! path.startsWith("/") ) {
path = "/" + path; path = "/" + path;

View file

@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.FreeMarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.FreeMarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.view.ViewObject; import edu.cornell.mannlib.vitro.webapp.view.ViewObject;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper; import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
@ -27,24 +28,14 @@ public class FreeMarkerSetup implements ServletContextListener {
ServletContext sc = event.getServletContext(); ServletContext sc = event.getServletContext();
// RY Change this to multi-location template scheme
String templatePath = sc.getRealPath("/templates/freemarker");
Configuration cfg = new Configuration(); Configuration cfg = new Configuration();
/* **** RY
Here's what I want to do to avoid having to pass in the contextPath to every view object created (in order for them to create their URLs):
Subclass Configuration. Add a static variable CONTEXT_PATH to it. Set that value here, and define a getter also. Then when creating
urls in view object methods like getUrl(), we can reference that configuration value. None of this is possible unless we can use
the method ServletContext.getContextPath(), new to Servlet API 2.5 andn therefore requiring tomcat 6 rather than 5.
*/
// Specify the data source where the template files come from. // Specify the data source where the template files come from.
try { // try {
cfg.setDirectoryForTemplateLoading(new File(templatePath)); // cfg.setDirectoryForTemplateLoading(new File(templatePath));
} catch (IOException e) { // } catch (IOException e) {
log.error("Error specifying template directory."); // log.error("Error specifying template directory.");
} // }
// RY This setting won't take effect until we use Configuration.getTemplate() to // RY This setting won't take effect until we use Configuration.getTemplate() to
// create templates. // create templates.
@ -69,6 +60,7 @@ public class FreeMarkerSetup implements ServletContextListener {
String contextPath = sc.getContextPath(); String contextPath = sc.getContextPath();
FreeMarkerHttpServlet.contextPath = contextPath; FreeMarkerHttpServlet.contextPath = contextPath;
ViewObject.contextPath = contextPath; ViewObject.contextPath = contextPath;
} }
public void contextDestroyed(ServletContextEvent event) { public void contextDestroyed(ServletContextEvent event) {

View file

@ -45,7 +45,6 @@ public class TabMenu extends MainMenu {
// come from the db should be accessible to the template to change the text. But we need them here // come from the db should be accessible to the template to change the text. But we need them here
// to apply the "active" mechanism. // to apply the "active" mechanism.
addItem("Index", "/browsecontroller"); addItem("Index", "/browsecontroller");
addItem("Index - FM", "/browse");
} }
} }

View file

@ -24,7 +24,6 @@
</#if> </#if>
<li><a href="${urls.about}$">About</a></li>, <li><a href="${urls.about}$">About</a></li>,
<li><a href="${urls.aboutFM}">About - FM</a></li>,
<#if urls.contact??> <#if urls.contact??>
<li><a href="${urls.contact}">Contact Us</a></li> <li><a href="${urls.contact}">Contact Us</a></li>
</#if> </#if>