NIHVIVO-658 Reintegrate changes from dev-freemarker-new. Most necessary updates to freemarker controllers for thread-safety are complete. A couple of details remain.

This commit is contained in:
rjy7 2010-06-24 21:51:46 +00:00
parent 7bf1bffd21
commit fb0377134b
18 changed files with 370 additions and 357 deletions

View file

@ -6,6 +6,7 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
@ -22,27 +23,29 @@ import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.shared.Lock;
import edu.cornell.mannlib.vedit.beans.LoginFormBean;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreeMarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.dao.jena.DependentResourceDeleteJena;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.EditN3Utils;
import freemarker.template.Configuration;
public class PrimitiveRdfEdit extends FreeMarkerHttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
// boolean loggedIn = checkLoginStatus(request, response);
// if( !loggedIn){
// doError(response,"You must be logged in to use this servlet.",HttpStatus.SC_UNAUTHORIZED);
// return;
// }
return mergeBodyToTemplate("primitiveRdfEdit.ftl",new HashMap());
return mergeBodyToTemplate("primitiveRdfEdit.ftl",new HashMap<String, Object>(), config);
}
@Override
protected String getTitle() {
protected String getTitle(String siteName) {
return "RDF edit";
}
@ -50,6 +53,7 @@ public class PrimitiveRdfEdit extends FreeMarkerHttpServlet{
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
VitroRequest vreq = new VitroRequest(request);
boolean loggedIn = checkLoginStatus(request, response);
if( !loggedIn){
doError(response,"You must be logged in to use this servlet.",HttpStatus.SC_UNAUTHORIZED);
@ -113,7 +117,7 @@ public class PrimitiveRdfEdit extends FreeMarkerHttpServlet{
//if not okay, send error message
doError(response,"Insufficent permissions.",HttpStatus.SC_UNAUTHORIZED);
}
if( hasPermission ){
String editorUri = EditN3Utils.getEditorUri(vreq,request.getSession(false),getServletContext());
try {

View file

@ -2,30 +2,33 @@
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import freemarker.template.Configuration;
public class AboutController extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(AboutController.class.getName());
protected String getTitle() {
return "About " + appName;
protected String getTitle(String siteName) {
return "About " + siteName;
}
protected String getBody() {
Map<String, Object> body = new HashMap<String, Object>();
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Portal portal = vreq.getPortal();
body.put("aboutText", portal.getAboutText());
body.put("acknowledgeText", portal.getAcknowledgeText());
String bodyTemplate = "about.ftl";
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}

View file

@ -10,6 +10,7 @@ import com.hp.hpl.jena.vocabulary.RDF;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
@ -18,6 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilterUtils;
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters;
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.VClassGroupTemplateModel;
import freemarker.template.Configuration;
import freemarker.template.SimpleSequence;
import org.apache.commons.logging.Log;
@ -62,13 +64,12 @@ public class BrowseController extends FreeMarkerHttpServlet {
_cacheRebuildThread.informOfQueueChange();
}
protected String getTitle() {
return "Index to " + portal.getAppName() + " Contents";
protected String getTitle(String siteName) {
return "Index to " + siteName + " Contents";
}
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Map<String, Object> body = new HashMap<String, Object>();
String bodyTemplate = "classGroups.ftl";
String message = null;
@ -77,6 +78,7 @@ public class BrowseController extends FreeMarkerHttpServlet {
//PortalFlag portalState= vreq.getPortalFlag();
int portalId = vreq.getPortal().getPortalId();
List<VClassGroup> groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), portalId);
if (groups == null || groups.isEmpty()) {
message = "There are not yet any items in the system.";
@ -96,7 +98,7 @@ public class BrowseController extends FreeMarkerHttpServlet {
body.put("message", message);
}
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}
public void destroy(){

View file

@ -20,6 +20,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.ContactMailServlet;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
import freemarker.template.Configuration;
/**
* Controller for comments ("contact us") page
@ -30,14 +31,14 @@ public class ContactFormController extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(ContactFormController.class.getName());
protected String getTitle() {
return appName + " Feedback Form";
protected String getTitle(String siteName) {
return siteName + " Feedback Form";
}
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Map<String, Object> body = new HashMap<String, Object>();
String bodyTemplate;
Portal portal = vreq.getPortal();
if (!ContactMailServlet.isSmtpHostConfigured()) {
body.put("errorMessage",
@ -54,9 +55,11 @@ public class ContactFormController extends FreeMarkerHttpServlet {
else {
ApplicationBean appBean = vreq.getAppBean();
ApplicationBean appBean = vreq.getAppBean();
String portalType = null;
int portalId = portal.getPortalId();
String appName = portal.getAppName();
if ( (appBean.getMaxSharedPortalId()-appBean.getMinSharedPortalId()) > 1
&& ( (portalId >= appBean.getMinSharedPortalId()
&& portalId <= appBean.getMaxSharedPortalId() )
@ -83,6 +86,6 @@ public class ContactFormController extends FreeMarkerHttpServlet {
bodyTemplate = "contactForm/form.ftl";
}
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}
}

View file

@ -26,6 +26,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import freemarker.template.Configuration;
public class ContactMailController extends FreeMarkerHttpServlet {
private static final Log log = LogFactory
@ -63,13 +66,13 @@ public class ContactMailController extends FreeMarkerHttpServlet {
return (host != null && host.length() > 0) ? host : null;
}
protected String getTitle() {
return appName + " Feedback Form";
protected String getTitle(String siteName) {
return siteName + " Feedback Form";
}
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Map<String, Object> body = new HashMap<String, Object>();
Portal portal = vreq.getPortal();
String bodyTemplate = null;
String statusMsg = null; // holds the error status
@ -157,13 +160,13 @@ public class ContactMailController extends FreeMarkerHttpServlet {
}
String msgText = composeEmail(webusername, webuseremail, comments,
deliveryfrom, originalReferer, vreq.getRemoteAddr());
deliveryfrom, originalReferer, vreq.getRemoteAddr(), config);
// Write the email to a backup file
try {
FileWriter fw = new FileWriter(context.getRealPath(EMAIL_BACKUP_FILE_PATH),true);
FileWriter fw = new FileWriter(getServletContext().getRealPath(EMAIL_BACKUP_FILE_PATH),true);
PrintWriter outFile = new PrintWriter(fw);
writeBackupCopy(outFile, msgText, spamReason);
writeBackupCopy(outFile, msgText, spamReason, config);
// Set the smtp host
Properties props = System.getProperties();
@ -209,7 +212,7 @@ public class ContactMailController extends FreeMarkerHttpServlet {
}
}
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}
@ -224,7 +227,7 @@ public class ContactMailController extends FreeMarkerHttpServlet {
private String composeEmail(String webusername, String webuseremail,
String comments, String deliveryfrom,
String originalReferer, String ipAddr) {
String originalReferer, String ipAddr, Configuration config) {
Map<String, Object> email = new HashMap<String, Object>();
String template = "contactForm/email.ftl";
@ -238,11 +241,11 @@ public class ContactMailController extends FreeMarkerHttpServlet {
email.put("referrer", UrlBuilder.urlDecode(originalReferer));
}
return mergeBodyToTemplate(template, email);
return mergeBodyToTemplate(template, email, config);
}
private void writeBackupCopy(PrintWriter outFile, String msgText,
String spamReason) {
String spamReason, Configuration config) {
Map<String, Object> backup = new HashMap<String, Object>();
String template = "contactForm/backup.ftl";
@ -256,7 +259,7 @@ public class ContactMailController extends FreeMarkerHttpServlet {
backup.put("msgText", msgText);
String backupText = mergeBodyToTemplate(template, backup);
String backupText = mergeBodyToTemplate(template, backup, config);
outFile.print(backupText);
outFile.flush();
//outFile.close();

View file

@ -2,15 +2,18 @@
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import freemarker.template.Configuration;
/**
* TEMPORARY for transition from JSP to FreeMarker. Once transition
* is complete and no more pages are generated in JSP, this can be removed.
@ -23,30 +26,37 @@ public class FreeMarkerComponentGenerator extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(FreeMarkerHttpServlet.class.getName());
private static ServletContext context = null;
FreeMarkerComponentGenerator(HttpServletRequest request, HttpServletResponse response) {
doSetup(request, response);
setUpPage();
}
public String getIdentity() {
return get("identity");
VitroRequest vreq = new VitroRequest(request);
Configuration config = getConfig(vreq);
// root is the map used to create the page shell - header, footer, menus, etc.
Map<String, Object> root = getSharedVariables(vreq);
setUpRoot(vreq, root);
request.setAttribute("ftl_identity", get("identity", root, config));
request.setAttribute("ftl_menu", get("menu", root, config));
request.setAttribute("ftl_search", get("search", root, config));
request.setAttribute("ftl_footer", get("footer", root, config));
}
public String getMenu() {
return get("menu");
}
public String getSearch() {
return get("search");
}
public String getFooter() {
return get("footer");
}
private String get(String templateName) {
private String get(String templateName, Map<String, Object> root, Configuration config) {
String template = "page/partials/" + templateName + ".ftl";
return mergeTemplateToRoot(template);
return mergeToTemplate(template, root, config).toString();
}
// RY We need the servlet context in getConfig(). For some reason using the method inherited from
// GenericServlet bombs.
public ServletContext getServletContext() {
return context;
}
protected static void setServletContext(ServletContext sc) {
context = sc;
}
}

View file

@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vedit.beans.LoginFormBean;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.controller.ContactMailServlet;
@ -32,7 +33,6 @@ import edu.cornell.mannlib.vitro.webapp.web.PortalWebUtil;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.filelist.ScriptList;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.filelist.StylesheetList;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.TabMenu;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
@ -51,26 +51,38 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
private static final Log log = LogFactory.getLog(FreeMarkerHttpServlet.class.getName());
private static final int FILTER_SECURITY_LEVEL = LoginFormBean.EDITOR;
protected static Configuration config = null;
protected static ServletContext context = null;
protected VitroRequest vreq;
protected HttpServletResponse response;
protected Portal portal;
protected int portalId;
protected String appName;
protected UrlBuilder urlBuilder;
private Map<String, Object> root = new HashMap<String, Object>();
public void doGet( HttpServletRequest request, HttpServletResponse response )
throws IOException, ServletException {
try {
super.doGet(request,response);
} catch (ServletException e) {
log.error("ServletException calling VitroHttpRequest.doGet()");
e.printStackTrace();
} catch (IOException e) {
log.error("IOException calling VitroHttpRequest.doGet()");
e.printStackTrace();
}
try {
doSetup(request, response);
setUpPage();
setTitleAndBody();
writePage();
VitroRequest vreq = new VitroRequest(request);
Configuration config = getConfig(vreq);
// We can't use shared variables in the FreeMarker configuration to store anything
// except theme-specific data, because multiple portals or apps might share the same theme. So instead
// just put the shared variables in both root and body.
Map<String, Object> sharedVariables = getSharedVariables(vreq); // start by getting the title here
// root is the map used to create the page shell - header, footer, menus, etc.
Map<String, Object> root = new HashMap<String, Object>(sharedVariables);
// body is the map used to create the page body
Map<String, Object> body = new HashMap<String, Object>(sharedVariables);
setUpRoot(vreq, root);
root.put("body", getBody(vreq, body, config)); // need config to get and process template
writePage(root, config, response);
} catch (Throwable e) {
log.error("FreeMarkerHttpServlet could not forward to view.");
@ -78,99 +90,79 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
log.error(e.getStackTrace());
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
// Basic setup needed by all FreeMarker controllers
protected void doSetup(HttpServletRequest request, HttpServletResponse response) {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
protected Configuration getConfig(VitroRequest vreq) {
if ( !(this instanceof FreeMarkerComponentGenerator) ) {
try {
super.doGet(request,response);
} catch (ServletException e) {
log.error("ServletException calling VitroHttpRequest.doGet()");
e.printStackTrace();
} catch (IOException e) {
log.error("IOException calling VitroHttpRequest.doGet()");
e.printStackTrace();
}
String themeDir = getThemeDir(vreq.getPortal());
return getConfigForTheme(themeDir);
}
@SuppressWarnings("unchecked")
protected Configuration getConfigForTheme(String themeDir) {
Map<String, Configuration> themeToConfigMap = (Map<String, Configuration>) (getServletContext().getAttribute("themeToConfigMap"));
if (themeToConfigMap.containsKey(themeDir)) {
return themeToConfigMap.get(themeDir);
} else {
Configuration config = getNewConfig(themeDir);
themeToConfigMap.put(themeDir, config);
return config;
}
vreq = new VitroRequest(request);
this.response = response;
portal = vreq.getPortal();
setTemplateLoader();
}
// Setup needed by all controllers that display a full page.
// Controllers that display parts of a page, respond to an Ajax request, etc., do not use this.
protected void setUpPage() {
private Configuration getNewConfig(String themeDir) {
urlBuilder = new UrlBuilder(portal);
Configuration config = new Configuration();
// RY Can this be removed? Do templates need it? Ideally, they should not.
// Only needed for some weird stuff in search box that I think is only used in old default theme.
// Some forms need it also, in which case it should get set in the getBody() method and passed to that
// body template.
portalId = portal.getPortalId();
root.put("portalId", portalId);
appName = portal.getAppName();
setSharedVariable("siteName", appName);
TabMenu menu = getTabMenu();
root.put("tabMenu", menu);
String buildEnv = ConfigurationProperties.getProperty("Environment.build");
if (buildEnv != null && buildEnv.equals("development")) {
config.setTemplateUpdateDelay(0); // no template caching in development
}
ApplicationBean appBean = vreq.getAppBean();
PortalWebUtil.populateSearchOptions(portal, appBean, vreq.getWebappDaoFactory().getPortalDao());
PortalWebUtil.populateNavigationChoices(portal, vreq, appBean, vreq.getWebappDaoFactory().getPortalDao());
root.put("tagline", portal.getShortHand());
root.put("breadcrumbs", BreadCrumbsUtil.getBreadCrumbsDiv(vreq));
String themeDir = getThemeDir();
setUrls(themeDir);
setLoginInfo();
setCopyrightInfo();
setThemeInfo(themeDir);
setScriptAndStylesheetObjects(themeDir);
setSharedVariable("dump", new edu.cornell.mannlib.vitro.webapp.web.directives.DumpDirective());
setSharedVariable("dumpDataModel", new edu.cornell.mannlib.vitro.webapp.web.directives.DumpDataModelDirective());
}
private void setScriptAndStylesheetObjects(String themeDir) {
// Use an object wrapper that exposes write methods, instead of the
// configuration's object wrapper, which doesn't, so the templates can
// add stylesheets and scripts to the lists by calling their add() methods.
// Specify how templates will see the data-model.
// The default wrapper exposes set methods unless exposure level is set.
// By default we want to block exposure of set methods.
// config.setObjectWrapper(new DefaultObjectWrapper());
BeansWrapper wrapper = new DefaultObjectWrapper();
wrapper.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
config.setObjectWrapper(wrapper);
// Set some formatting defaults. These can be overridden at the template
// or environment (template-processing) level, or for an individual
// instance by using built-ins.
config.setLocale(java.util.Locale.US);
String dateFormat = "M/d/yyyy";
config.setDateFormat(dateFormat);
String timeFormat = "hh:mm a";
config.setTimeFormat(timeFormat);
config.setDateTimeFormat(dateFormat + " " + timeFormat);
//config.setNumberFormat("#,##0.##");
try {
// Here themeDir SHOULD NOT have the context path already added to it.
TemplateModel stylesheets = wrapper.wrap(new StylesheetList(themeDir));
setSharedVariable("stylesheets", stylesheets);
TemplateModel scripts = wrapper.wrap(new ScriptList());
setSharedVariable("scripts", scripts);
} catch (TemplateModelException e) {
log.error("Error creating stylesheet and script TemplateModels");
config.setSetting("url_escaping_charset", "ISO-8859-1");
} catch (TemplateException e) {
log.error("Error setting value for url_escaping_charset.");
}
config.setTemplateLoader(getTemplateLoader(config, themeDir));
return config;
}
// 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.
protected final void setTemplateLoader() {
protected final TemplateLoader getTemplateLoader(Configuration config, String themeDir) {
String themeTemplateDir = context.getRealPath(getThemeDir()) + "/templates";
ServletContext context = getServletContext();
String themeTemplateDir = context.getRealPath(themeDir) + "/templates";
String vitroTemplateDir = context.getRealPath("/templates/freemarker");
try {
@ -179,24 +171,55 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "");
TemplateLoader[] loaders = new TemplateLoader[] { themeFtl, vitroFtl, ctl };
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);
config.setTemplateLoader(mtl);
return mtl;
} catch (IOException e) {
log.error("Error loading templates");
return null;
}
}
private TabMenu getTabMenu() {
return new TabMenu(vreq, portalId);
// We can't use shared variables in the FreeMarker configuration to store anything
// except theme-specific data, because multiple portals or apps might share the same theme. So instead
// we'll get all the shared variables here, and put them in both root and body maps.
protected Map<String, Object> getSharedVariables(VitroRequest vreq) {
Map<String, Object> map = new HashMap<String, Object>();
Portal portal = vreq.getPortal();
// Ideally, templates wouldn't need portal id. Currently used as a hidden input value
// in the site search box, so needed for now.
map.put("portalId", portal.getPortalId());
String siteName = portal.getAppName();
map.put("siteName", siteName);
map.put("title", getTitle(siteName));
String themeDir = getThemeDir(portal);
UrlBuilder urlBuilder = new UrlBuilder(portal);
map.put("urls", getUrls(themeDir, urlBuilder));
// This value will be available to any template as a path for adding a new stylesheet.
// It does not contain the context path, because the methods to generate the href
// attribute from the string passed in by the template automatically add the context path.
map.put("stylesheetDir", themeDir + "/css");
map.put("stylesheets", getStylesheetList(themeDir));
map.put("scripts", getScriptList());
addDirectives(map);
return map;
}
public String getThemeDir() {
public String getThemeDir(Portal portal) {
return portal.getThemeDir().replaceAll("/$", "");
}
// Define the URLs that are accessible to the templates. Note that we do not create menus here,
// because we want the templates to be free to define the link text and where the links are displayed.
private final void setUrls(String themeDir) {
private final Map<String, String> getUrls(String themeDir, UrlBuilder urlBuilder) {
// The urls that are accessible to the templates.
// NB We are not using our menu object mechanism to build menus here, because we want the
// view to control which links go where, and the link text and title.
@ -204,11 +227,6 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
urls.put("home", urlBuilder.getHomeUrl());
String bannerImage = portal.getBannerImage();
if ( ! StringUtils.isEmpty(bannerImage)) {
root.put("bannerImage", UrlBuilder.getUrl(themeDir + "site_icons/" + bannerImage));
}
urls.put("about", urlBuilder.getPortalUrl(Route.ABOUT));
if (ContactMailServlet.getSmtpHostFromProperties() != null) {
urls.put("contact", urlBuilder.getPortalUrl(Route.CONTACT));
@ -218,11 +236,86 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
urls.put("login", urlBuilder.getPortalUrl(Route.LOGIN));
urls.put("logout", urlBuilder.getLogoutUrl());
urls.put("siteAdmin", urlBuilder.getPortalUrl(Route.LOGIN));
urls.put("siteIcons", urlBuilder.getPortalUrl(themeDir + "/site_icons"));
return urls;
}
private TemplateModel getStylesheetList(String themeDir) {
setSharedVariable("urls", urls);
// For script and stylesheet lists, use an object wrapper that exposes write methods,
// instead of the configuration's object wrapper, which doesn't. The templates can
// add stylesheets and scripts to the lists by calling their add() methods.
BeansWrapper wrapper = new DefaultObjectWrapper();
try {
// Here themeDir SHOULD NOT have the context path already added to it.
return wrapper.wrap(new StylesheetList(themeDir));
} catch (TemplateModelException e) {
log.error("Error creating stylesheet TemplateModel");
return null;
}
}
private TemplateModel getScriptList() {
// For script and stylesheet lists, use an object wrapper that exposes write methods,
// instead of the configuration's object wrapper, which doesn't. The templates can
// add stylesheets and scripts to the lists by calling their add() methods.
BeansWrapper wrapper = new DefaultObjectWrapper();
try {
return wrapper.wrap(new ScriptList());
} catch (TemplateModelException e) {
log.error("Error creating script TemplateModel");
return null;
}
}
// Add any Java directives the templates should have access to
private void addDirectives(Map<String, Object> map) {
map.put("dump", new edu.cornell.mannlib.vitro.webapp.web.directives.DumpDirective());
map.put("dumpDataModel", new edu.cornell.mannlib.vitro.webapp.web.directives.DumpDataModelDirective());
}
// Add variables that should be available only to the page's root map, not to the body.
// RY This is protected instead of private so FreeMarkerComponentGenerator can access.
// Once we don't need that (i.e., jsps have been eliminated) we can make it private.
protected void setUpRoot(VitroRequest vreq, Map<String, Object> root) {
root.put("tabMenu", getTabMenu(vreq));
Portal portal = vreq.getPortal();
ApplicationBean appBean = vreq.getAppBean();
PortalWebUtil.populateSearchOptions(portal, appBean, vreq.getWebappDaoFactory().getPortalDao());
PortalWebUtil.populateNavigationChoices(portal, vreq, appBean, vreq.getWebappDaoFactory().getPortalDao());
addLoginInfo(vreq, root);
root.put("copyright", getCopyrightInfo(portal));
root.put("siteTagline", portal.getShortHand());
root.put("breadcrumbs", BreadCrumbsUtil.getBreadCrumbsDiv(vreq));
String themeDir = getThemeDir(portal);
// This value is used only in stylesheets.ftl and already contains the context path.
root.put("stylesheetPath", UrlBuilder.getUrl(themeDir + "/css"));
String bannerImage = portal.getBannerImage();
if ( ! StringUtils.isEmpty(bannerImage)) {
root.put("bannerImage", UrlBuilder.getUrl(themeDir + "site_icons/" + bannerImage));
}
}
private TabMenu getTabMenu(VitroRequest vreq) {
// RY There's a vreq.getPortalId() method, but not sure if it returns
// same value as this.
int portalId = vreq.getPortal().getPortalId();
return new TabMenu(vreq, portalId);
}
private final void setLoginInfo() {
private final void addLoginInfo(VitroRequest vreq, Map<String, Object> root) {
String loginName = null;
int securityLevel;
@ -246,79 +339,43 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
}
}
private final void setCopyrightInfo() {
private final Map<String, Object> getCopyrightInfo(Portal portal) {
Map<String, Object> copyright = null;
String copyrightText = portal.getCopyrightAnchor();
if ( ! StringUtils.isEmpty(copyrightText) ) {
Map<String, Object> copyright = new HashMap<String, Object>();
copyright = new HashMap<String, Object>();
copyright.put("text", copyrightText);
int thisYear = Calendar.getInstance().get(Calendar.YEAR); // use ${copyrightYear?c} in template
//String thisYear = ((Integer)Calendar.getInstance().get(Calendar.YEAR)).toString(); // use ${copyrightYear} in template
//SimpleDate thisYear = new SimpleDate(Calendar.getInstance().getTime(), TemplateDateModel.DATE); // use ${copyrightYear?string("yyyy")} in template
copyright.put("year", thisYear);
copyright.put("url", portal.getCopyrightURL());
root.put("copyright", copyright);
}
return copyright;
}
private final void setThemeInfo(String themeDir) {
// This value will be available to any template as a path for adding a new stylesheet.
// It does not contain the context path, because the methods to generate the href
// attribute from the string passed in by the template automatically add the context path.
setSharedVariable("stylesheetDir", themeDir + "/css");
String themeDirWithContext = UrlBuilder.getUrl(themeDir);
// This value is used only in stylesheets.ftl and already contains the context path.
root.put("stylesheetPath", themeDirWithContext + "/css");
setSharedVariable("siteIconPath", themeDirWithContext + "/site_icons");
}
// Default case is to set title first, because it's used in the body. However, in some cases
// the title is based on values computed during compilation of the body (e.g., IndividualListController).
// Individual controllers can override this method to set title and body together. End result must be:
// body is added to root with key "body"
// title is set as a shared variable with key "title"
// This can be achieved by making sure setBody() and setTitle() are called.
protected void setTitleAndBody() {
setTitle();
setBody();
}
// protected void setTitleAndBody() {
// setTitle();
// setBody();
// }
protected void setBody() {
root.put("body", getBody());
}
protected String getBody() {
return ""; // body should never be null
// Subclasses will override. This serves as a default.
protected String getTitle(String siteName) {
return siteName;
}
protected void setTitle() {
String title = getTitle();
// If the individual controller fails to assign a non-null, non-empty title
if (StringUtils.isEmpty(title)) {
title = appName;
}
// Title is a shared variable because it's used in both body and head elements.
setSharedVariable("title", title);
}
protected String getTitle() {
return "";
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
return "";
}
protected void setSharedVariable(String key, Object value) {
try {
config.setSharedVariable(key, value);
} catch (TemplateModelException e) {
log.error("Can't set shared variable '" + key + "'.");
}
}
protected StringWriter mergeToTemplate(String templateName, Map<String, Object> map) {
protected StringWriter mergeToTemplate(String templateName, Map<String, Object> map, Configuration config) {
Template template = null;
try {
@ -341,28 +398,28 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
return sw;
}
protected String mergeBodyToTemplate(String templateName, Map<String, Object> map) {
protected String mergeBodyToTemplate(String templateName, Map<String, Object> map, Configuration config) {
templateName = "body/" + templateName;
String body = mergeToTemplate(templateName, map).toString();
String body = mergeToTemplate(templateName, map, config).toString();
return body;
}
protected void writePage() {
protected void writePage(Map<String, Object> root, Configuration config, HttpServletResponse response) {
String templateName = "page/" + getPageTemplateName();
writeTemplate(templateName, root);
writeTemplate(templateName, root, config, response);
}
protected void ajaxWrite(String templateName, Map<String, Object> map) {
protected void ajaxWrite(String templateName, Map<String, Object> map, Configuration config, HttpServletResponse response) {
templateName = "ajax/" + templateName;
writeTemplate(templateName, map);
writeTemplate(templateName, map, config, response);
}
protected void writeTemplate(String templateName, Map<String, Object> map) {
StringWriter sw = mergeToTemplate(templateName, map);
write(sw);
protected void writeTemplate(String templateName, Map<String, Object> map, Configuration config, HttpServletResponse response) {
StringWriter sw = mergeToTemplate(templateName, map, config);
write(sw, response);
}
protected void write(StringWriter sw) {
protected void write(StringWriter sw, HttpServletResponse response) {
try {
PrintWriter out = response.getWriter();
@ -378,27 +435,12 @@ public class FreeMarkerHttpServlet extends VitroHttpServlet {
return "default.ftl";
}
public static boolean isConfigured() {
return config != null;
}
// TEMPORARY methods for transition from JSP to FreeMarker. Once transition
// is complete and no more pages are generated in JSP, this can be removed.
// Do this if FreeMarker is configured (i.e., not Datastar) and if we are not in
// a FreeMarkerHttpServlet, which will generate identity, menu, and footer from the page template.
// TEMPORARY method for transition from JSP to FreeMarker.
// It's a static method because it needs to be called from JSPs that don't go through a servlet.
public static void getFreeMarkerComponentsForJsp(HttpServletRequest request, HttpServletResponse response) {
FreeMarkerComponentGenerator fcg = new FreeMarkerComponentGenerator(request, response);
request.setAttribute("ftl_identity", fcg.getIdentity());
request.setAttribute("ftl_menu", fcg.getMenu());
request.setAttribute("ftl_search", fcg.getSearch());
request.setAttribute("ftl_footer", fcg.getFooter());
}
// This method is called by FreeMarkerComponentGenerator, since root is private.
// Don't want to make root protected because other controllers shouldn't add to it.
protected String mergeTemplateToRoot(String template) {
return mergeToTemplate(template, root).toString();
// We need to create a FreeMarkerHttpServlet object in order to call the instance methods
// to set up the data model.
new FreeMarkerComponentGenerator(request, response);
}
}

View file

@ -2,6 +2,8 @@
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.util.HashMap;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
@ -9,12 +11,8 @@ import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException;
public class FreeMarkerSetup implements ServletContextListener {
@ -23,55 +21,10 @@ public class FreeMarkerSetup implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
ServletContext sc = event.getServletContext();
Configuration cfg = new Configuration();
// Specify the data source where the template files come from.
// RY Now being done for each request, in order to support multi-portal apps
// and dynamic theme-loading.
// try {
// cfg.setDirectoryForTemplateLoading(new File(templatePath));
// } catch (IOException e) {
// log.error("Error specifying template directory.");
// }
String buildEnv = ConfigurationProperties.getProperty("Environment.build");
if (buildEnv != null && buildEnv.equals("development")) {
cfg.setTemplateUpdateDelay(0); // no template caching in development
}
// Specify how templates will see the data-model.
// The default wrapper exposes set methods unless exposure level is set.
// By default we want to block exposure of set methods.
// cfg.setObjectWrapper(new DefaultObjectWrapper());
BeansWrapper wrapper = new DefaultObjectWrapper();
wrapper.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
cfg.setObjectWrapper(wrapper);
// Set some formatting defaults. These can be overridden at the template
// or environment (template-processing) level, or for an individual
// instance by using built-ins.
cfg.setLocale(java.util.Locale.US);
String dateFormat = "M/d/yyyy";
cfg.setDateFormat(dateFormat);
String timeFormat = "hh:mm a";
cfg.setTimeFormat(timeFormat);
cfg.setDateTimeFormat(dateFormat + " " + timeFormat);
//cfg.setNumberFormat("#,##0.##");
try {
cfg.setSetting("url_escaping_charset", "ISO-8859-1");
} catch (TemplateException e) {
log.error("Error setting value for url_escaping_charset.");
}
FreeMarkerHttpServlet.config = cfg;
FreeMarkerHttpServlet.context = sc;
sc.setAttribute("themeToConfigMap", new HashMap<String, Configuration>());
BaseTemplateModel.context = sc;
FreeMarkerComponentGenerator.setServletContext(sc);
UrlBuilder.contextPath = sc.getContextPath();
}
public void contextDestroyed(ServletContextEvent event) {

View file

@ -13,8 +13,10 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.IndividualTemplateModel;
import freemarker.template.Configuration;
/**
* Generates a list of individuals for display in a template
@ -23,23 +25,18 @@ public class IndividualListController extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(IndividualListController.class.getName());
private VClass vclass = null;
private String title = null;
protected void setTitleAndBody() {
setBody();
}
// private VClass vclass = null;
// private String title = null;
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Map<String, Object> body = new HashMap<String, Object>();
String bodyTemplate = "individualList.ftl";
String errorMessage = null;
String message = null;
try {
Object obj = vreq.getAttribute("vclass");
vclass = null;
VClass vclass = null;
if ( obj == null ) { // look for vitroclass id parameter
String vitroClassIdStr = vreq.getParameter("vclassId");
if ( !StringUtils.isEmpty(vitroClassIdStr)) {
@ -77,13 +74,15 @@ public class IndividualListController extends FreeMarkerHttpServlet {
}
// Set title and subtitle. Title will be retrieved later in getTitle().
VClassGroup classGroup = vclass.getGroup();
VClassGroup classGroup = vclass.getGroup();
String title;
if (classGroup == null) {
title = vclass.getName();
} else {
title = classGroup.getPublicName();
body.put("subtitle", vclass.getName());
}
body.put("title", title);
body.put("individuals", individuals);
}
@ -100,16 +99,8 @@ public class IndividualListController extends FreeMarkerHttpServlet {
} else if (message != null) {
body.put("message", message);
}
setTitle();
return mergeBodyToTemplate(bodyTemplate, body);
}
protected String getTitle() {
// The title is determined during compilation of the body, so we put it in an instance variable
// to be retrieved later.
return title;
return mergeBodyToTemplate(bodyTemplate, body, config);
}
private class HelpException extends Throwable {

View file

@ -27,7 +27,9 @@ import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.web.DisplayVocabulary;
import freemarker.template.Configuration;
public class NavigationController extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
@ -45,7 +47,7 @@ public class NavigationController extends FreeMarkerHttpServlet {
}
@Override
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
OntModel displayOntModel = (OntModel)getServletContext().getAttribute("displayOntModel");
OntModel jenaOntModel = (OntModel)getServletContext().getAttribute("jenaOntModel");
@ -54,7 +56,7 @@ public class NavigationController extends FreeMarkerHttpServlet {
Map<String,Object> values = getValues(ind, displayOntModel,jenaOntModel, getValuesFromRequest(/*?*/) );
String template = getTemplate(ind, displayOntModel);
return mergeBodyToTemplate(template, values);
return mergeBodyToTemplate(template, values, config);
}
private Map<String,Object>getValuesFromRequest(){
@ -158,12 +160,6 @@ public class NavigationController extends FreeMarkerHttpServlet {
void configure( Map<String,String> config);
Map<String,Object> getValues(OntModel model, Map<String,Object> values);
}
@Override
protected String getTitle() {
// TODO Auto-generated method stub
return super.getTitle();
}
private class NavigationURLPatternListener extends StatementListener {
private Map<Pattern,String> urlPatternToURI;

View file

@ -8,28 +8,31 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
import freemarker.template.Configuration;
public class TermsOfUseController extends FreeMarkerHttpServlet {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(TermsOfUseController.class.getName());
protected String getTitle() {
return appName + " Terms of Use";
protected String getTitle(String siteName) {
return siteName + " Terms of Use";
}
protected String getBody() {
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
Map<String, Object> body = new HashMap<String, Object>();
Portal portal = vreq.getPortal();
String rootBreadCrumbAnchor = portal.getRootBreadCrumbAnchor();
String websiteName = StringUtils.isEmpty(rootBreadCrumbAnchor) ? appName : rootBreadCrumbAnchor;
String websiteName = StringUtils.isEmpty(rootBreadCrumbAnchor) ? portal.getAppName() : rootBreadCrumbAnchor;
body.put("websiteName", websiteName);
body.put("copyrightAnchor", portal.getCopyrightAnchor());
String bodyTemplate = "termsOfUse.ftl";
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}
}

View file

@ -9,6 +9,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import freemarker.template.Configuration;
/**
* A place for storing test cases.
* @author rjy7
@ -22,9 +25,7 @@ public class TestController extends FreeMarkerHttpServlet {
return "Test";
}
protected String getBody() {
Map<String, Object> body = new HashMap<String, Object>();
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
// Test of #list directive in template on undefined, null, and empty values.
// Basic idea: empty list okay, null or undefined value not okay.
@ -68,7 +69,7 @@ public class TestController extends FreeMarkerHttpServlet {
// Create the template to see the examples live.
String bodyTemplate = "test.ftl";
return mergeBodyToTemplate(bodyTemplate, body);
return mergeBodyToTemplate(bodyTemplate, body, config);
}

View file

@ -47,6 +47,7 @@ import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc;
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneIndexer;
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup;
import edu.cornell.mannlib.vitro.webapp.utils.FlagMathUtils;
import freemarker.template.Configuration;
/**
* AutocompleteController is used to generate autocomplete and select element content
@ -93,17 +94,18 @@ public class AutocompleteController extends FreeMarkerHttpServlet implements Sea
String templateName = request.getServletPath().equals("/autocomplete") ? "autocompleteResults.ftl" : "selectResults.ftl";
Map<String, Object> map = new HashMap<String, Object>();
VitroRequest vreq = new VitroRequest(request);
Configuration config = getConfig(vreq);
PortalFlag portalFlag = vreq.getPortalFlag();
try {
doSetup(request, response);
PortalFlag portalFlag = vreq.getPortalFlag();
// make sure an IndividualDao is available
if( vreq.getWebappDaoFactory() == null
|| vreq.getWebappDaoFactory().getIndividualDao() == null ){
log.error("makeUsableBeans() could not get IndividualDao ");
doSearchError(templateName, map);
doSearchError(templateName, map, config, response);
return;
}
IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
@ -121,7 +123,7 @@ public class AutocompleteController extends FreeMarkerHttpServlet implements Sea
List<String> urisToExclude = Arrays.asList(vreq.getParameterValues("filter"));
if (query == null ) {
doNoQuery(templateName, map);
doNoQuery(templateName, map, config, response);
return;
}
@ -138,20 +140,20 @@ public class AutocompleteController extends FreeMarkerHttpServlet implements Sea
topDocs = searcherForRequest.search(query,null,maxHitSize);
}catch (Exception ex){
log.error(ex);
doFailedSearch(templateName, map);
doFailedSearch(templateName, map, config, response);
return;
}
}
if( topDocs == null || topDocs.scoreDocs == null){
log.error("topDocs for a search was null");
doFailedSearch(templateName, map);
doFailedSearch(templateName, map, config, response);
return;
}
int hitsLength = topDocs.scoreDocs.length;
if ( hitsLength < 1 ){
doFailedSearch(templateName, map);
doFailedSearch(templateName, map, config, response);
return;
}
log.debug("found "+hitsLength+" hits");
@ -178,11 +180,11 @@ public class AutocompleteController extends FreeMarkerHttpServlet implements Sea
Collections.sort(results);
map.put("results", results);
ajaxWrite(templateName, map);
ajaxWrite(templateName, map, config, response);
} catch (Throwable e) {
log.error("AutocompleteController(): " + e);
doSearchError(templateName, map);
doSearchError(templateName, map, config, response);
return;
}
}
@ -333,16 +335,16 @@ public class AutocompleteController extends FreeMarkerHttpServlet implements Sea
}
private void doNoQuery(String templateName, Map<String, Object> map) {
ajaxWrite(templateName, map);
private void doNoQuery(String templateName, Map<String, Object> map, Configuration config, HttpServletResponse response) {
ajaxWrite(templateName, map, config, response);
}
private void doFailedSearch(String templateName, Map<String, Object> map) {
ajaxWrite(templateName, map);
private void doFailedSearch(String templateName, Map<String, Object> map, Configuration config, HttpServletResponse response) {
ajaxWrite(templateName, map, config, response);
}
private void doSearchError(String templateName, Map<String, Object> map) {
ajaxWrite(templateName, map);
private void doSearchError(String templateName, Map<String, Object> map, Configuration config, HttpServletResponse response) {
ajaxWrite(templateName, map, config, response);
}
public static final int MAX_QUERY_LENGTH = 500;

View file

@ -3,7 +3,7 @@
<#-- Contact form submission confirmation page -->
<h2>Thank you for your feedback</h2>
<img src="${siteIconPath}/mail.gif" alt="mailbox"/><br/>
<img src="${urls.siteIcons}/mail.gif" alt="mailbox"/><br/>
<p>Thank you for contacting our curation and development team. We will respond to your inquiry as soon as possible.</p>
<p>Return to the <a href="${urls.home}">home page</a>.</p>

View file

@ -4,7 +4,7 @@
<h2>${title}</h2>
<img src="${siteIconPath}/bomb.gif" alt="email error"/>
<img src="${urls.siteIcons}/bomb.gif" alt="email error"/>
<p class="normal">An error occurred during the processing of your request.<br />
<#if errorMessage?has_content>

View file

@ -4,8 +4,8 @@
<div id="footer">
<#if bannerImageUrl??>
<img class="footerLogo" src="${urls.bannerImage}" alt="${tagline!}" />
<#if urls.bannerImage??>
<img class="footerLogo" src="${urls.bannerImage}" alt="${siteTagline!}" />
</#if>
<div class="footerLinks">

View file

@ -6,8 +6,8 @@
<h1><a title="Home" href="${urls.home}">${siteName}</a></h1>
<#if tagline.has_content>
<em>${tagline}</em>
<#if siteTagline.has_content>
<em>${siteTagline}</em>
</#if>
<ul id="otherMenu">

View file

@ -7,7 +7,7 @@
<#if showFlag1SearchField??>
<select id="search-form-modifier" name="flag1" class="form-item" >
<option value="nofiltering" selected="selected">entire database (${loginName})</option>
<option value="${portalId}">${tagline!}</option>
<option value="${portalId}">${siteTagline!}</option>
</select>
<#else>
<input type="hidden" name="flag1" value="${portalId}" />