NIHVIVO-1304 Rewrote widget framework to support a single template per widget, with multiple macros in each template file.

This commit is contained in:
rjy7 2010-11-15 02:11:08 +00:00
parent 965f4c399b
commit 8a49574f7e
7 changed files with 88 additions and 63 deletions

View file

@ -18,10 +18,11 @@ public class LoginWidget extends Widget {
private static final Log log = LogFactory.getLog(LoginWidget.class); private static final Log log = LogFactory.getLog(LoginWidget.class);
@Override @Override
protected WidgetTemplateValues process(Environment env, Map params, String widgetName, HttpServletRequest request, ServletContext context) { protected WidgetTemplateValues process(Environment env, Map params,
HttpServletRequest request, ServletContext context) {
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
map.put("fruit", "bananas"); map.put("fruit", "bananas");
return new WidgetTemplateValues (getMarkupTemplateName(widgetName), map); return new WidgetTemplateValues (getMarkupMacroName(), map);
} }
} }

View file

@ -13,10 +13,11 @@ import freemarker.core.Environment;
public class TestWidget extends Widget { public class TestWidget extends Widget {
@Override @Override
protected WidgetTemplateValues process(Environment env, Map params, String widgetName, HttpServletRequest request, ServletContext context) { protected WidgetTemplateValues process(Environment env, Map params,
HttpServletRequest request, ServletContext context) {
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
map.put("fruit", "bananas"); map.put("fruit", "bananas");
return new WidgetTemplateValues (getMarkupTemplateName(widgetName), map); return new WidgetTemplateValues (getMarkupMacroName(), map);
} }
} }

View file

@ -3,6 +3,7 @@
package edu.cornell.mannlib.vitro.webapp.web.widgets; package edu.cornell.mannlib.vitro.webapp.web.widgets;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -14,9 +15,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import freemarker.cache.TemplateLoader;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.Configuration; import freemarker.core.Macro;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateHashModel; import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelException;
@ -31,18 +31,12 @@ public abstract class Widget {
public String doAssets(Environment env, Map params) { public String doAssets(Environment env, Map params) {
String widgetName = params.get("name").toString(); //getWidgetName(); String widgetName = params.get("name").toString(); //getWidgetName();
String templateName = getAssetsTemplateName(widgetName); Macro assetsMacro = getMacroFromTemplate(getAssetsMacroName(), widgetName, env);
// Allow the assets template to be absent without generating an error.
TemplateLoader templateLoader = env.getConfiguration().getTemplateLoader();
try {
if ( templateLoader.findTemplateSource(templateName) == null ) {
return "";
}
} catch (IOException e) {
log.error("Error finding template source", e);
}
// Allow there to be no assets macro in the template
if (assetsMacro == null) {
return "";
}
TemplateHashModel dataModel = env.getDataModel(); TemplateHashModel dataModel = env.getDataModel();
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
@ -54,30 +48,36 @@ public abstract class Widget {
log.error("Error getting asset values from data model."); log.error("Error getting asset values from data model.");
} }
return processTemplateToString(widgetName, env, templateName, map); return processMacroToString(env, widgetName, assetsMacro, map);
} }
public String doMarkup(Environment env, Map params) { public String doMarkup(Environment env, Map params) {
HttpServletRequest request = (HttpServletRequest) env.getCustomAttribute("request"); HttpServletRequest request = (HttpServletRequest) env.getCustomAttribute("request");
ServletContext context = (ServletContext) env.getCustomAttribute("context"); ServletContext context = (ServletContext) env.getCustomAttribute("context");
WidgetTemplateValues values = process(env, params, request, context);
String widgetName = params.get("name").toString(); // getWidgetName(); String widgetName = params.get("name").toString(); // getWidgetName();
WidgetTemplateValues values = process(env, params, widgetName, request, context); return processMacroToString(env, widgetName, values);
return processTemplateToString(widgetName, env, values);
} }
// Default assets template name. Can be overridden by subclasses. // Default template name. Can be overridden by subclasses.
protected String getAssetsTemplateName(String widgetName) { protected String getTemplateName(String widgetName) {
return "widget-" + widgetName + "-assets.ftl"; return "widget-" + widgetName + ".ftl";
}
// Default markup template name. Can be overridden in subclasses, or assigned
// differently in the subclass process() method. For example, LoginWidget will
// select a template according to login processing status.
protected String getMarkupTemplateName(String widgetName) {
return "widget-" + widgetName + "-markup.ftl";
} }
// Default assets macro name. Can be overridden by subclasses.
protected String getAssetsMacroName() {
return "assets";
}
// Default markup macro name. Can be overridden by subclasses, or
// subclass process() method can select from various markup macros
// based on widget state. For example, the login widget markup macro will
// differ depending on login processing state.
protected String getMarkupMacroName() {
return "markup";
}
// private String getWidgetName() { // private String getWidgetName() {
// String name = this.getClass().getName(); // String name = this.getClass().getName();
// name= name.replaceAll(".*\\.", ""); // name= name.replaceAll(".*\\.", "");
@ -86,31 +86,57 @@ public abstract class Widget {
// return name; // return name;
// } // }
protected abstract WidgetTemplateValues process(Environment env, Map params, String widgetName, HttpServletRequest request, ServletContext context); protected abstract WidgetTemplateValues process(Environment env, Map params,
HttpServletRequest request, ServletContext context);
private String processTemplateToString(String widgetName, Environment env, String templateName, Map<String, Object> map) { private String processMacroToString(Environment env, String widgetName, Macro macro, Map<String, Object> map) {
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
Configuration config = env.getConfiguration();
try { try {
Template template = config.getTemplate(templateName); String templateString = macro.getChildNodes().get(0).toString();
// NB Using this method of creating a template from a string does not allow the widget template to import
// other templates (but it can include other templates). We'd need to use a StringTemplateLoader
// in the config instead. See StringTemplateLoader API doc.
// The problem is that the StringTemplateLoader has to be added to the config's MultiTemplateLoader.
// Then to support multi-threading, we can't just add the widget here to the StringTemplateLoader with
// the same key, e.g., "widgetTemplate", since one putTemplate() call will clobber a previous one.
// We need to give each widget macro template a unique key in the StringTemplateLoader, and check
// if it's already there or else add it. Leave this for later.
Template template = new Template("widget", new StringReader(templateString), env.getConfiguration());
template.process(map, out); template.process(map, out);
} catch (Throwable th) { } catch (Throwable th) {
log.error("Could not process widget " + widgetName, th); log.error("Could not process widget " + widgetName, th);
} }
return out.toString(); return out.toString();
} }
private String processTemplateToString(String widgetName, Environment env, WidgetTemplateValues values) { private String processMacroToString(Environment env, String widgetName, String macroName, Map<String, Object> map) {
return processTemplateToString(widgetName, env, values.getTemplateName(), values.getMap()); Macro macro = getMacroFromTemplate(macroName, widgetName, env);
return processMacroToString(env, widgetName, macro, map);
}
private String processMacroToString(Environment env, String widgetName, WidgetTemplateValues values) {
return processMacroToString(env, widgetName, values.getMacroName(), values.getMap());
}
private Macro getMacroFromTemplate(String macroName, String widgetName, Environment env) {
String templateName = getTemplateName(widgetName);
Template template = null;
Macro macro = null;
try {
template = env.getConfiguration().getTemplate(templateName);
macro = (Macro)template.getMacros().get(macroName);
} catch (IOException e) {
log.error("Cannot get template " + templateName);
}
return macro;
} }
protected static class WidgetTemplateValues { protected static class WidgetTemplateValues {
private final String templateName; private final String macroName;
private final Map<String, Object> map; private final Map<String, Object> map;
public WidgetTemplateValues(String templateName, Map<String, Object> map) { public WidgetTemplateValues(String templateName, Map<String, Object> map) {
this.templateName = templateName; this.macroName = templateName;
this.map = map; this.map = map;
} }
@ -123,8 +149,8 @@ public abstract class Widget {
return Collections.unmodifiableMap(this.map); return Collections.unmodifiableMap(this.map);
} }
public String getTemplateName() { public String getMacroName() {
return this.templateName; return this.macroName;
} }
} }

View file

@ -11,11 +11,11 @@
<li> elements. An <li> element may span multiple lines. <li> elements. An <li> element may span multiple lines.
Usage: Usage:
<@firstLastList> <@firstLastList />
<li>apples</li> <li>apples</li>
<li>bananas</li> <li>bananas</li>
<li>oranges</li> <li>oranges</li>
</@firstLastList> </@firstLastList />
RY Consider rewriting in Java. Probably designers won't want to modify this. That would allow us to support RY Consider rewriting in Java. Probably designers won't want to modify this. That would allow us to support
nested <li> elements. nested <li> elements.
@ -47,19 +47,19 @@
indicate how to split the text. indicate how to split the text.
Usage: Usage:
<@firstLastListNested> <@firstLastListNested />
<li>apples</li>, <li>apples</li>,
<li>bananas</li>, <li>bananas</li>,
<li>oranges</li> <li>oranges</li>
</@firstLastListNested> </@firstLastListNested />
<@firstLastListNested delim="??"> <@firstLastListNested delim="??" />
<li>apples, oranges</li>?? <li>apples, oranges</li>??
<li>bananas, lemons</li>?? <li>bananas, lemons</li>??
<li>grapefruit, limes</li> <li>grapefruit, limes</li>
</@firstLastListNested> </@firstLastListNested />
<@firstLastListNested delim="??"> <@firstLastListNested delim="??" />
<li>Books <li>Books
<ul> <ul>
<li>Persuasion</li> <li>Persuasion</li>
@ -72,7 +72,7 @@
<li>Time</li> <li>Time</li>
</ul> </ul>
</li> </li>
</@firstLastListNested> </@firstLastListNested />
RY Consider rewriting in Java. Probably designers won't want to modify this. RY Consider rewriting in Java. Probably designers won't want to modify this.
--> -->

View file

@ -1,5 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
${stylesheets.add("/css/test.css")}
${scripts.add("/js/testscript.js")}
${headScripts.add("/js/testheadscript.js")}

View file

@ -1,6 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<div class="testWidget">
<h4>This is the test widget.</h4>
<p>I like ${fruit}.</p>
</div>

View file

@ -9,8 +9,16 @@
</#macro> </#macro>
<#macro markup> <#macro markup>
<#import "lib-list.ftl" as l>
<div class="testWidget"> <div class="testWidget">
<h4>This is the test widget.</h4> <h4>This is the test widget using macros.</h4>
<p>I like ${fruit}.</p> <p>I like ${fruit}.</p>
</div> </div>
</#macro>
<#macro altMarkup>
<div class="testWidget">
<h4>This is the alternate version of the test widget.</h4>
<p>I hate ${fruit}.</p>
</div>
</#macro> </#macro>