Move FreeMarker templates, controllers, setup, and library upgrades into trunk. Not used to generate any pages or templates yet (except a couple of hidden pages that are only available through the url).

This commit is contained in:
rjy7 2010-05-11 20:37:48 +00:00
parent 72ee86f0a7
commit a0c93fda6d
44 changed files with 2256 additions and 7 deletions

View file

@ -138,6 +138,12 @@
</listener-class>
</listener>
<listener>
<listener-class>
edu.cornell.mannlib.vitro.webapp.servlet.setup.FreeMarkerSetup
</listener-class>
</listener>
<!--
<listener>
<listener-class>
@ -267,6 +273,15 @@
<url-pattern>/about</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>AboutControllerFM</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.AboutControllerFM</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AboutControllerFM</servlet-name>
<url-pattern>/about-fm</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FakeSelfEditController</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.FakeSelfEditController</servlet-class>
@ -781,6 +796,15 @@
<url-pattern>/entitylist</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>IndividualListControllerFM</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.IndividualListControllerFM</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IndividualListControllerFM</servlet-name>
<url-pattern>/individuallistFM</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SearchController</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.search.controller.PagedSearchController</servlet-class>
@ -948,6 +972,12 @@
<load-on-startup>5</load-on-startup>
</servlet>
<servlet>
<servlet-name>browsecontrollerFM</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerFM</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
<servlet>
<servlet-name>pubsbyorg</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.vclass.PubsByDepartmentServlet</servlet-class>
@ -1064,6 +1094,10 @@
<servlet-name>browsecontroller</servlet-name>
<url-pattern>/browsecontroller</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>browsecontrollerFM</servlet-name>
<url-pattern>/browse</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>pubsbyorg</servlet-name>
<url-pattern>/pubsbyorg</url-pattern>

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
webapp/lib/freemarker.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -45,7 +45,8 @@ public class VClass extends BaseResourceBean implements Comparable<VClass>
// [bjl23 2007-08-12] Yep. A count of individuals in the class.
private int myEntityCount = -1;
@Deprecated
// rjy7 Removing deprecation since currently we have no other means to get this value.
// @Deprecated
public int getEntityCount() { return myEntityCount; }
public void setEntityCount( int ec ) { myEntityCount = ec; }
@ -107,8 +108,8 @@ public class VClass extends BaseResourceBean implements Comparable<VClass>
public VClass( String namespace, String localName, String vclassName )
{
myName = vclassName;
namespace = namespace;
localName = localName;
this.namespace = namespace;
this.localName = localName;
URI = namespace + localName;
}

View file

@ -0,0 +1,41 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.FreeMarkerHttpServlet;
import freemarker.template.SimpleDate;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateModelException;
public class AboutControllerFM extends FreeMarkerHttpServlet {
private static final Log log = LogFactory.getLog(AboutControllerFM.class.getName());
protected String getTitle() {
return "About " + portal.getAppName();
}
protected String getBody() {
Map<String, Object> body = new HashMap<String, Object>();
body.put("aboutText", portal.getAboutText());
body.put("acknowledgeText", portal.getAcknowledgeText());
String templateName = "body/about.ftl";
return mergeBodyToTemplate(templateName, body);
}
}

View file

@ -0,0 +1,322 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.listeners.StatementListener;
import com.hp.hpl.jena.rdf.model.Statement;
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.dao.VClassGroupDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.filtering.WebappDaoFactoryFiltering;
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.controller.FreeMarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.view.VClassGroupView;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import freemarker.template.*;
import javax.servlet.ServletContext;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class BrowseControllerFM extends FreeMarkerHttpServlet {
static final long serialVersionUID=2006030721126L;
private transient ConcurrentHashMap<Integer, List> _groupListMap
= new ConcurrentHashMap<Integer, List>();
private transient ConcurrentLinkedQueue<String> _rebuildQueue
= new ConcurrentLinkedQueue<String>();
private RebuildGroupCacheThread _cacheRebuildThread;
private static final Log log = LogFactory.getLog(BrowseControllerFM.class.getName());
public void init(javax.servlet.ServletConfig servletConfig)
throws javax.servlet.ServletException {
super.init(servletConfig);
ServletContext sContext = servletConfig.getServletContext();
//BJL23: I'll work on a strategy for avoiding all this craziness.
OntModel model = (OntModel)sContext.getAttribute("jenaOntModel");
OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel");
OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel");
BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this);
model.register(bccl);
baseModel.register(bccl);
infModel.register(bccl);
_rebuildQueue.add(REBUILD_EVERY_PORTAL);
_cacheRebuildThread = new RebuildGroupCacheThread(this);
_cacheRebuildThread.setDaemon(true);
_cacheRebuildThread.start();
_cacheRebuildThread.informOfQueueChange();
}
protected String getTitle() {
return "Index to " + portal.getAppName() + " Contents";
}
protected String getBody() {
Map body = new HashMap();
// Set main page template attributes specific to this page
root.put("contentClass", "siteMap");
if( vreq.getParameter("clearcache") != null ) //mainly for debugging
clearGroupCache();
//PortalFlag portalState= vreq.getPortalFlag();
String message = "";
List<VClassGroup> groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId());
if (groups == null || groups.isEmpty()) {
message = "There are not yet any items in the system.";
body.put("message", message);
}
else {
List<VClassGroupView> vcgroups = new ArrayList<VClassGroupView>();
Iterator<VClassGroup> i = groups.iterator();
VClassGroup group;
VClassGroupView displayGroup;
while (i.hasNext()) {
group = (VClassGroup) i.next();
displayGroup = new VClassGroupView(group);
vcgroups.add(displayGroup);
}
body.put("classGroups", vcgroups);
}
String templateName = "body/browseGroups.ftl";
return mergeBodyToTemplate(templateName, body);
}
public void destroy(){
_cacheRebuildThread.kill();
}
private List getGroups( VClassGroupDao vcgDao, int portalId ){
List grp = _groupListMap.get(portalId);
if( grp == null ){
log.debug("needed to build vclassGroups for portal " + portalId);
// Get all classgroups, each populated with a list of their member vclasses
List groups = vcgDao.getPublicGroupsWithVClasses(ORDER_BY_DISPLAYRANK, !INCLUDE_UNINSTANTIATED);
// now cull out the groups with no populated classes
//removeUnpopulatedClasses( groups);
vcgDao.removeUnpopulatedGroups(groups);
_groupListMap.put(portalId, groups);
return groups;
} else {
return grp;
}
}
private static boolean ORDER_BY_DISPLAYRANK = true;
private static boolean INCLUDE_UNINSTANTIATED = true;
// private void removeUnpopulatedClasses( List<VClassGroup> groups){
// if( groups == null || groups.size() == 0 ) return;
// for( VClassGroup grp : groups ){
// ListIterator it = grp.listIterator();
// while(it.hasNext()){
// VClass claz = (VClass)it.next();
// if( claz.getEntityCount() < 1 )
// it.remove();
// }
// }
// }
void requestCacheUpdate(String portalUri){
log.debug("requesting update for portal " + portalUri);
_rebuildQueue.add(portalUri);
_cacheRebuildThread.informOfQueueChange();
}
protected synchronized void refreshGroupCache() {
long start = System.currentTimeMillis();
try{
boolean rebuildAll = false;
HashSet<String> portalURIsToRebuild = new HashSet<String>();
String portalUri;
while ( null != (portalUri = _rebuildQueue.poll()) ){
if( portalUri.equals(REBUILD_EVERY_PORTAL)){
rebuildAll = true;
_rebuildQueue.clear();
break;
}else{
portalURIsToRebuild.add(portalUri);
}
}
ServletContext sContext = getServletConfig().getServletContext();
ApplicationBean appBean = new ApplicationBean();
WebappDaoFactory wdFactory = (WebappDaoFactory)sContext.getAttribute("webappDaoFactory");
if( wdFactory == null ){
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
return;
}
Collection<Portal> portals;
if( rebuildAll ){
portals = wdFactory.getPortalDao().getAllPortals();
} else {
portals = new LinkedList<Portal>();
for( String uri : portalURIsToRebuild){
Portal p =wdFactory.getPortalDao().getPortalByURI(uri);
if( p!= null)
portals.add(wdFactory.getPortalDao().getPortalByURI(uri));
}
}
for(Portal portal : portals){
rebuildCacheForPortal(portal,appBean,wdFactory);
}
log.info("rebuilt ClassGroup cache in " + (System.currentTimeMillis() - start) + " msec");
}catch (Exception ex){
log.error("could not rebuild cache", ex);
}
}
protected synchronized void rebuildCacheForPortalUri(String uri){
ServletContext sContext = getServletConfig().getServletContext();
WebappDaoFactory wdFactory = (WebappDaoFactory)sContext.getAttribute("webappDaoFactory");
if( wdFactory == null ){
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
return;
}
ApplicationBean appBean = new ApplicationBean();
Portal portal = wdFactory.getPortalDao().getPortalByURI(uri);
rebuildCacheForPortal(portal,appBean,wdFactory);
}
protected synchronized void rebuildCacheForPortal(Portal portal, ApplicationBean appBean, WebappDaoFactory wdFactory){
VitroFilters vFilters = null;
if( portal.isFlag1Filtering() ){
PortalFlag pflag = new PortalFlag(portal.getPortalId());
if( vFilters == null)
vFilters = VitroFilterUtils.getFilterFromPortalFlag(pflag);
else
vFilters = vFilters.and( VitroFilterUtils.getFilterFromPortalFlag(pflag));
}
WebappDaoFactory filteringDaoFactory ;
if( vFilters !=null ){
filteringDaoFactory = new WebappDaoFactoryFiltering(wdFactory,vFilters);
}else{
filteringDaoFactory = wdFactory;
}
_groupListMap.remove(portal.getPortalId());
getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId());
}
private void clearGroupCache(){
_groupListMap = new ConcurrentHashMap<Integer, List>();
}
/* ****************** Jena Model Change Listener***************************** */
private class BrowseControllerChangeListener extends StatementListener {
private BrowseControllerFM controller = null;
public BrowseControllerChangeListener(BrowseControllerFM controller){
this.controller=controller;
}
public void addedStatement(Statement stmt) {
checkAndDoUpdate(stmt);
}
public void removedStatement(Statement stmt) {
checkAndDoUpdate(stmt);
}
private void checkAndDoUpdate(Statement stmt){
if( stmt==null ) return;
if( log.isDebugEnabled()){
log.debug("subject: " + stmt.getSubject().getURI());
log.debug("predicate: " + stmt.getPredicate().getURI());
}
if( RDF.type.getURI().equals( stmt.getPredicate().getURI()) ){
requestCacheUpdate(REBUILD_EVERY_PORTAL);
} else if( VitroVocabulary.PORTAL_FLAG1FILTERING.equals( stmt.getPredicate().getURI())){
requestCacheUpdate(stmt.getSubject().getURI());
} else if( VitroVocabulary.IN_CLASSGROUP.equals( stmt.getPredicate().getURI() )){
requestCacheUpdate(REBUILD_EVERY_PORTAL);
}
}
}
/* ******************** RebuildGroupCacheThread **************** */
protected class RebuildGroupCacheThread extends Thread {
BrowseControllerFM controller;
boolean die = false;
boolean queueChange = false;
long queueChangeMills = 0;
private boolean awareOfQueueChange = false;
RebuildGroupCacheThread(BrowseControllerFM controller) {
this.controller = controller;
}
public void run() {
while(true){
try{
synchronized (this){
if( _rebuildQueue.isEmpty() ){
log.debug("rebuildGroupCacheThread.run() -- queye empty, sleep");
wait(1000 * 60 );
}
if( die ) {
log.debug("doing rebuildGroupCacheThread.run() -- die()");
return;
}
if( queueChange && !awareOfQueueChange){
log.debug("rebuildGroupCacheThread.run() -- awareOfQueueChange, delay start of rebuild");
awareOfQueueChange = true;
wait(200);
}
}
if( awareOfQueueChange && System.currentTimeMillis() - queueChangeMills > 200){
log.debug("rebuildGroupCacheThread.run() -- refreshGroupCache()");
controller.refreshGroupCache();
synchronized( this){
queueChange = false;
}
awareOfQueueChange = false;
}else {
synchronized( this ){
wait(200);
}
}
} catch(InterruptedException e){}
}
}
synchronized void informOfQueueChange(){
queueChange = true;
queueChangeMills = System.currentTimeMillis();
this.notifyAll();
}
synchronized void kill(){
die = true;
notifyAll();
}
}
protected static String REBUILD_EVERY_PORTAL ="Rebuild every portal.";
}

View file

@ -67,6 +67,7 @@ public class Controllers {
public static final String ALPHA_INDEX_JSP = "/templates/alpha/alphaIndex.jsp";
public static final String SEARCH_URL = "/search";
public static final String SEARCH_BASIC_JSP = "/templates/search/searchBasic.jsp";
public static final String SEARCH_PAGED_JSP = "/templates/search/searchPaged.jsp";
public static final String SEARCH_FAILED_JSP = "/templates/search/searchFailed.jsp";
@ -87,6 +88,8 @@ public class Controllers {
public static final Object SEARCH_ERROR_JSP = "/search_error.jsp";
public static final String CONTACT_URL = "/comments";
//public static final String TAB_ENTITIES_LIST_JSP = "templates/tab/tabEntities.jsp";
private static List letters = null;

View file

@ -0,0 +1,292 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
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.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
import edu.cornell.mannlib.vitro.webapp.view.menu.TabMenu;
import edu.cornell.mannlib.vitro.webapp.web.PortalWebUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModelException;
public class FreeMarkerHttpServlet extends VitroHttpServlet {
private static final Log log = LogFactory.getLog(FreeMarkerHttpServlet.class.getName());
private static final int FILTER_SECURITY_LEVEL = LoginFormBean.EDITOR;
public static Configuration config = null;
public static String contextPath = null; // RY or do we need to store the entire ServletContext?
protected VitroRequest vreq;
protected HttpServletResponse response;
protected Portal portal;
protected Map<String, Object> root = new HashMap<String, Object>();
// Some servlets have their own doGet() method, in which case they need to call
// doSetup(), setTitle(), setBody(), and write() themselves. Other servlets define only
// a getBody() and getTitle() method and use the parent doGet() method.
public void doGet( HttpServletRequest request, HttpServletResponse response )
throws IOException, ServletException {
try {
doSetup(request, response);
setTitle();
setBody();
write(response);
} catch (Throwable e) {
log.error("FreeMarkerHttpServlet could not forward to view.");
log.error(e.getMessage());
log.error(e.getStackTrace());
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
protected void setBody() {
root.put("body", getBody());
}
protected void setSharedVariable(String key, String value) {
try {
config.setSharedVariable(key, value);
} catch (TemplateModelException e) {
log.error("Can't set shared variable '" + key + "'.");
}
}
protected void setTitle() {
setSharedVariable("title", getTitle());
}
protected String getTitle() {
return null;
}
protected String getBody() {
return null;
}
protected StringWriter mergeToTemplate(String templateName, Map<String, Object> map) {
Template template = null;
try {
template = config.getTemplate(templateName);
} catch (IOException e) {
log.error("Cannot get template " + templateName);
}
StringWriter sw = new StringWriter();
if (template != null) {
try {
template.process(map, sw);
} catch (TemplateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return sw;
}
protected String mergeBodyToTemplate(String templateName, Map<String, Object> map) {
String body = mergeToTemplate(templateName, map).toString();
extractLinkTagsFromBody(body);
return body;
}
// This is the only way to do this in FreeMarker. We cannot: (1) put a sequence of stylesheets in the template
// context which the template can add to, because the template cannot call methods on the container. The template
// can create a container but not add to one.
// (2) create a sequence of stylesheets or a scalar to hold the name of a stylesheet in the template, because
// it does not get passed back to the controller. The template can create only local variables.
// *** RY But we can create a view object with an add method, that the templates could use to add to the
// list. ***
private String extractLinkTagsFromBody(String body) {
List<String> links = new ArrayList<String>();
String re = "<link[^>]*>";
Pattern pattern = Pattern.compile(re);
Matcher matcher = pattern.matcher(body);
while (matcher.find()) {
links.add(matcher.group());
}
root.put("stylesheets", links); // SIDE-EFFECT
body = matcher.replaceAll("");
return body;
}
protected void write(HttpServletResponse response) {
String templateName = "page/default.ftl";
StringWriter sw = mergeToTemplate(templateName, root);
try {
PrintWriter out = response.getWriter();
out.print(sw);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doSetup(HttpServletRequest request, HttpServletResponse response) {
try {
super.doGet(request,response);
} catch (ServletException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
vreq = new VitroRequest(request);
this.response = response;
portal = vreq.getPortal();
// 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.
int portalId = portal.getPortalId();
try {
config.setSharedVariable("portalId", portalId);
} catch (TemplateModelException e) {
log.error("Can't set shared variable 'portalId'.");
}
TabMenu menu = getTabMenu(portalId);
root.put("tabMenu", menu);
ApplicationBean appBean = vreq.getAppBean();
PortalWebUtil.populateSearchOptions(portal, appBean, vreq.getWebappDaoFactory().getPortalDao());
PortalWebUtil.populateNavigationChoices(portal, vreq, appBean, vreq.getWebappDaoFactory().getPortalDao());
// We'll need to separate theme-general and theme-specific stylesheet
// dirs, so we need either two attributes or a list.
String themeDir = portal.getThemeDir();
String stylesheetDir = getUrl(themeDir + "css/");
try {
config.setSharedVariable("stylesheetDir", stylesheetDir);
} catch (TemplateModelException e) {
log.error("Can't set shared variable 'stylesheetDir'.");
}
root.put("siteName", portal.getAppName());
root.put("tagline", portal.getShortHand());
setUrls(portalId, themeDir);
setLoginInfo();
setCopyrightInfo();
}
// 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(int portalId, String themeDir) {
// 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.
Map<String, String> urls = new HashMap<String, String>();
String homeUrl = (portal.getRootBreadCrumbURL()!=null && portal.getRootBreadCrumbURL().length()>0) ?
portal.getRootBreadCrumbURL() : vreq.getContextPath()+"/";
urls.put("home", homeUrl);
String bannerImage = portal.getBannerImage();
if ( ! StringUtils.isEmpty(bannerImage)) {
root.put("bannerImage", getUrl(themeDir + "site_icons/" + bannerImage));
}
urls.put("about", getUrl(Controllers.ABOUT + "?home=" + portalId));
urls.put("aboutFM", getUrl(Controllers.ABOUT + "-fm?home=" + portalId)); // TEMPORARY
if (ContactMailServlet.getSmtpHostFromProperties() != null) {
urls.put("contact", getUrl(Controllers.CONTACT_URL + "?home=" + portalId));
}
urls.put("search", getUrl(Controllers.SEARCH_URL));
urls.put("termsOfUse", getUrl("/termsOfUse?home=" + portalId));
urls.put("login", getUrl(Controllers.LOGIN));
urls.put("logout", getUrl(Controllers.LOGOUT));
urls.put("siteAdmin", getUrl(Controllers.SITE_ADMIN));
root.put("urls", urls);
}
private final void setLoginInfo() {
String loginName = null;
int securityLevel;
HttpSession session = vreq.getSession();
LoginFormBean loginBean = (LoginFormBean) session.getAttribute("loginHandler");
if (loginBean != null && loginBean.testSessionLevel(vreq) > -1) {
loginName = loginBean.getLoginName();
securityLevel = Integer.parseInt(loginBean.getLoginRole());
}
if (loginName != null) {
root.put("loginName", loginName);
securityLevel = Integer.parseInt(loginBean.getLoginRole());
if (securityLevel >= FILTER_SECURITY_LEVEL) {
ApplicationBean appBean = vreq.getAppBean();
if (appBean.isFlag1Active()) {
root.put("showFlag1SearchField", true);
}
}
}
}
private final void setCopyrightInfo() {
String copyrightText = portal.getCopyrightAnchor();
if ( ! StringUtils.isEmpty(copyrightText) ) {
Map<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);
}
}
public static String getUrl(String path) {
if ( ! path.startsWith("/") ) {
path = "/" + path;
}
return contextPath + path;
}
private TabMenu getTabMenu(int portalId) {
return new TabMenu(vreq, portalId);
}
}

View file

@ -0,0 +1,681 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.datatypes.TypeMapper;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapperFactory;
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
import edu.cornell.mannlib.vitro.webapp.web.jsptags.StringProcessorTag;
/**
* Handles requests for entity information.
* Calls EntityPropertyListController to draw property list.
*
* @author bdc34
*
*/
public class IndividualControllerFM extends VitroHttpServlet {
private static final Log log = LogFactory.getLog(IndividualControllerFM.class.getName());
private String default_jsp = Controllers.BASIC_JSP;
private String default_body_jsp = Controllers.ENTITY_JSP;
private ApplicationBean appBean;
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws IOException, ServletException {
try {
super.doGet(req, res);
VitroRequest vreq = new VitroRequest(req);
//get URL without hostname or servlet context
String url = req.getRequestURI().substring(req.getContextPath().length());
//Check to see if the request is for a non-information resource, redirect if it is.
String redirectURL = checkForRedirect ( url, req.getHeader("accept") );
if( redirectURL != null ){
doRedirect( req, res, redirectURL );
return;
}
ContentType rdfFormat = checkForLinkedDataRequest(url,req.getHeader("accept"));
if( rdfFormat != null ){
doRdf( vreq, res, rdfFormat );
return;
}
Individual indiv = null;
try{
indiv = getEntityFromRequest( vreq);
}catch(Throwable th){
doHelp(res);
return;
}
if( indiv == null || checkForHidden(vreq, indiv) || checkForSunset(vreq, indiv)){
doNotFound(vreq, res);
return;
}
doHtml( vreq, res , indiv);
return;
} catch (Throwable e) {
log.error(e);
req.setAttribute("javax.servlet.jsp.jspException",e);
RequestDispatcher rd = req.getRequestDispatcher("/error.jsp");
rd.forward(req, res);
}
}
private void doHtml(VitroRequest vreq, HttpServletResponse res, Individual indiv) throws ServletException, IOException {
IndividualDao iwDao = vreq.getWebappDaoFactory().getIndividualDao();
ObjectPropertyDao opDao = vreq.getWebappDaoFactory().getObjectPropertyDao();
//Check if a "relatedSubjectUri" parameter has been supplied, and,
//if so, retrieve the related individual.t
//Some individuals make little sense standing alone and should
//be displayed in the context of their relationship to another.
String relatedSubjectUri = vreq.getParameter("relatedSubjectUri");
if (relatedSubjectUri != null) {
Individual relatedSubjectInd = iwDao.getIndividualByURI(relatedSubjectUri);
if (relatedSubjectInd != null) {
vreq.setAttribute("relatedSubject", relatedSubjectInd);
}
}
String relatingPredicateUri = vreq.getParameter("relatingPredicateUri");
if (relatingPredicateUri != null) {
ObjectProperty relatingPredicateProp = opDao.getObjectPropertyByURI(relatingPredicateUri);
if (relatingPredicateProp != null) {
vreq.setAttribute("relatingPredicate", relatingPredicateProp);
}
}
indiv.setKeywords(iwDao.getKeywordsForIndividualByMode(indiv.getURI(),"visible"));
indiv.sortForDisplay();
String vclassName = "unknown";
String customView = null;
String customCss = null;
if( indiv.getVClass() != null ){
vclassName = indiv.getVClass().getName();
List<VClass> clasList = indiv.getVClasses(true);
for (VClass clas : clasList) {
customView = clas.getCustomDisplayView();
if (customView != null) {
if (customView.length()>0) {
vclassName = clas.getName(); // reset entity vclassname to name of class where a custom view
log.debug("Found direct class ["+clas.getName()+"] with custom view "+customView+"; resetting entity vclassName to this class");
break;
} else {
customView = null;
}
}
}
if (customView == null) { //still
clasList = indiv.getVClasses(false);
for (VClass clas : clasList) {
customView = clas.getCustomDisplayView();
if (customView != null) {
if (customView.length()>0) {
// note that NOT changing entity vclassName here yet
log.debug("Found inferred class ["+clas.getName()+"] with custom view "+customView);
break;
} else {
customView = null;
}
}
}
}
} else {
log.error("Entity " + indiv.getURI() + " with vclass URI " +
indiv.getVClassURI() + ", no vclass with that URI exists");
}
if (customView!=null) {
// insert test for whether a css files of the same name exists, and populate the customCss string for use when construction the header
}
String netid = iwDao.getNetId(indiv.getURI());
vreq.setAttribute("netid", netid);
vreq.setAttribute("vclassName", vclassName);
vreq.setAttribute("entity",indiv);
Portal portal = vreq.getPortal();
vreq.setAttribute("portal",String.valueOf(portal));
String view= getViewFromRequest(vreq);
if( view == null){
if (customView == null) {
view = default_jsp;
vreq.setAttribute("bodyJsp","/"+Controllers.ENTITY_JSP);
log.debug("no custom view and no view parameter in request for rendering "+indiv.getName());
} else {
view = default_jsp;
log.debug("setting custom view templates/entity/"+ customView + " for rendering "+indiv.getName());
vreq.setAttribute("bodyJsp", "/templates/entity/"+customView);
}
vreq.setAttribute("entityPropsListJsp",Controllers.ENTITY_PROP_LIST_JSP);
vreq.setAttribute("entityDatapropsListJsp",Controllers.ENTITY_DATAPROP_LIST_JSP);
vreq.setAttribute("entityMergedPropsListJsp",Controllers.ENTITY_MERGED_PROP_LIST_GROUPED_JSP);
vreq.setAttribute("entityKeywordsListJsp",Controllers.ENTITY_KEYWORDS_LIST_JSP);
} else {
log.debug("Found view parameter "+view+" in request for rendering "+indiv.getName());
}
//set title before we do the highlighting so we don't get markup in it.
vreq.setAttribute("title",indiv.getName());
//setup highlighter for search terms
checkForSearch(vreq, indiv);
// set CSS and script elements
String contextPath = "";
if (vreq.getContextPath().length()>1) {
contextPath = vreq.getContextPath();
}
String css = "<link rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\""
+ contextPath + "/" + portal.getThemeDir() + "css/entity.css\"/>\n"
+ "<script language='JavaScript' type='text/javascript' src='"+contextPath+"/js/toggle.js'></script> \n";
if (customCss!=null) {
css += customCss;
}
if( indiv.getURI().startsWith( vreq.getWebappDaoFactory().getDefaultNamespace() )){
vreq.setAttribute("entityLinkedDataURL", indiv.getURI() + "/" + indiv.getLocalName() + ".rdf");
}
// generate link to RDF representation for semantic web clients like Piggy Bank
// BJL 2008-07-16: I'm temporarily commenting this out because I forgot we need to make sure it filters out the hidden properties
// generate url for this entity
// String individualToRDF = "http://"+vreq.getServerName()+":"+vreq.getServerPort()+vreq.getContextPath()+"/entity?home=1&uri="+forURL(entity.getURI())+"&view=rdf.rdf";
//css += "<link rel='alternate' type='application/rdf+xml' title='"+entity.getName()+"' href='"+individualToRDF+"' />";
vreq.setAttribute("css",css);
vreq.setAttribute("scripts", "/templates/entity/entity_inject_head.jsp");
RequestDispatcher rd = vreq.getRequestDispatcher( view );
rd.forward(vreq,res);
}
private void doRdf(VitroRequest vreq, HttpServletResponse res,
ContentType rdfFormat) throws IOException, ServletException {
Individual indiv = getEntityFromRequest(vreq);
if( indiv == null ){
doNotFound(vreq, res);
return;
}
OntModel ontModel = null;
HttpSession session = vreq.getSession(false);
if( session != null )
ontModel =(OntModel)session.getAttribute("jenaOntModel");
if( ontModel == null)
ontModel = (OntModel)getServletContext().getAttribute("jenaOntModel");
Model newModel;
newModel = getRDF(indiv, ontModel, ModelFactory.createDefaultModel(), 0);
res.setContentType(rdfFormat.getMediaType());
String format = "";
if ( RDFXML_MIMETYPE.equals(rdfFormat.getMediaType()))
format = "RDF/XML";
else if( N3_MIMETYPE.equals(rdfFormat.getMediaType()))
format = "N3";
else if ( TTL_MIMETYPE.equals(rdfFormat.getMediaType()))
format ="TTL";
newModel.write( res.getOutputStream(), format );
}
private void doRedirect(HttpServletRequest req, HttpServletResponse res,
String redirectURL) {
// It seems like there must be a better way to do this
String hn = req.getHeader("Host");
res.setHeader("Location", res.encodeURL( "http://" + hn + req.getContextPath() + redirectURL ));
res.setStatus(res.SC_SEE_OTHER);
}
private static Pattern LINKED_DATA_URL = Pattern.compile("^/individual/([^/]*)$");
private static Pattern NS_PREFIX_URL = Pattern.compile("^/individual/([^/]*)/([^/]*)$");
/**
Gets the entity id from the request.
Works for the following styles of URLs:
/individual?id=individualLocalName
/individual?entityId=individualLocalName
/individual?uri=urlencodedURI
/individual?nedit=bdc34
/individual?nedIt=bdc34
/individual/nsprefix/localname
/individual/localname
/individual/localname/localname.rdf
/individual/localname/localname.n3
/individual/localname/localname.ttl
/individual/localname/localname.html
@return null on failure.
*/
public static Individual getEntityFromRequest(VitroRequest vreq) {
String netIdStr = null;
Individual entity = null;
IndividualDao iwDao = vreq.getWebappDaoFactory().getIndividualDao();
String entityIdStr = vreq.getParameter("id");
if (entityIdStr == null || entityIdStr.equals(""))
entityIdStr = vreq.getParameter("entityId");
if( entityIdStr != null){
try {
String entityURI = vreq.getWebappDaoFactory().getDefaultNamespace()+"individual"+entityIdStr;
entity = iwDao.getIndividualByURI(entityURI);
} catch ( Exception e ) {
log.warn("Could not parse entity id: " + entityIdStr);
return null;
}
return entity;
}
String entityURIStr = vreq.getParameter("uri");
if (entityURIStr != null) {
try {
entity = iwDao.getIndividualByURI(entityURIStr);
} catch (Exception e) {
log.warn("Could not retrieve entity "+entityURIStr);
return null;
}
return entity;
}
//get URL without hostname or servlet context
String url = vreq.getRequestURI().substring(vreq.getContextPath().length());
/* check for parts of URL that indicate request for RDF
http://vivo.cornell.edu/individual/n23/n23.rdf
http://vivo.cornell.edu/individual/n23/n23.n3
http://vivo.cornell.edu/individual/n23/n23.ttl */
String uri = null;
Matcher m = RDF_REQUEST.matcher(url);
if( m.matches() && m.groupCount() == 1)
uri = m.group(1);
m = N3_REQUEST.matcher(url);
if( m.matches() && m.groupCount() == 1)
uri = m.group(1);
m = TTL_REQUEST.matcher(url);
if( m.matches() && m.groupCount() == 1)
uri= m.group(1);
m = HTML_REQUEST.matcher(url);
if( m.matches() && m.groupCount() == 1)
uri= m.group(1);
if( uri != null )
return iwDao.getIndividualByURI(vreq.getWebappDaoFactory().getDefaultNamespace() + uri);
// see if we can get the URI from a name space prefix and a local name
Matcher prefix_match = NS_PREFIX_URL.matcher(url);
if( prefix_match.matches() && prefix_match.groupCount() == 2){
String prefix = prefix_match.group(1);
String localName = prefix_match.group(2);
//String[] requestParts = requestURI.split("/individual/");
//String[] URIParts = requestParts[1].split("/");
//String localName = URIParts[1];
String namespace = "";
NamespaceMapper namespaceMapper = NamespaceMapperFactory.getNamespaceMapper(vreq.getSession().getServletContext());
String t;
namespace = ( (t = namespaceMapper.getNamespaceForPrefix(prefix)) != null) ? t : "";
return iwDao.getIndividualByURI(namespace+localName);
}
// see if we can get a local name
Matcher linkedDataMatch = LINKED_DATA_URL.matcher(url);
if( linkedDataMatch.matches() && linkedDataMatch.groupCount() == 1){
String localName = linkedDataMatch.group(1);
String ns = vreq.getWebappDaoFactory().getDefaultNamespace();
return iwDao.getIndividualByURI( ns + localName );
}
//so we try to get the netid
netIdStr = vreq.getParameter("netId");
if (netIdStr==null || netIdStr.equals(""))
netIdStr = vreq.getParameter("netid");
if ( netIdStr != null ){
uri = iwDao.getIndividualURIFromNetId(netIdStr);
return iwDao.getIndividualByURI(uri);
}
return null;
}
private static Pattern URI_PATTERN = Pattern.compile("^/individual/([^/]*)$");
//Redirect if the request is for http://hostname/individual/localname
// if accept is nothing or text/html redirect to ???
// if accept is some RDF thing redirect to the URL for RDF
private String checkForRedirect(String url, String acceptHeader) {
Matcher m = URI_PATTERN.matcher(url);
if( m.matches() && m.groupCount() == 1 ){
ContentType c = checkForLinkedDataRequest(url, acceptHeader);
if( c != null ){
String redirectUrl = "/individual/" + m.group(1) + "/" + m.group(1) ;
if( RDFXML_MIMETYPE.equals( c.getMediaType()) ){
return redirectUrl + ".rdf";
}else if( N3_MIMETYPE.equals( c.getMediaType() )){
return redirectUrl + ".n3";
}else if( TTL_MIMETYPE.equals( c.getMediaType() )){
return redirectUrl + ".ttl";
}//else send them to html
}
//else redirect to HTML representation
return "/display/" + m.group(1) ;
}else{
return null;
}
}
private static Pattern RDF_REQUEST = Pattern.compile("^/individual/([^/]*)/\\1.rdf$");
private static Pattern N3_REQUEST = Pattern.compile("^/individual/([^/]*)/\\1.n3$");
private static Pattern TTL_REQUEST = Pattern.compile("^/individual/([^/]*)/\\1.ttl$");
private static Pattern HTML_REQUEST = Pattern.compile("^/display/([^/]*)$");
/**
* @return null if this is not a linked data request, returns content type if it is a
* linked data request.
*/
private ContentType checkForLinkedDataRequest(String url, String acceptHeader) {
try {
//check the accept header
if (acceptHeader != null) {
List<ContentType> actualContentTypes = new ArrayList<ContentType>();
actualContentTypes.add(new ContentType( XHTML_MIMETYPE ));
actualContentTypes.add(new ContentType( HTML_MIMETYPE ));
actualContentTypes.add(new ContentType( RDFXML_MIMETYPE ));
actualContentTypes.add(new ContentType( N3_MIMETYPE ));
actualContentTypes.add(new ContentType( TTL_MIMETYPE ));
ContentType best = ContentType.getBestContentType(acceptHeader,actualContentTypes);
if (best!=null && (
RDFXML_MIMETYPE.equals(best.getMediaType()) ||
N3_MIMETYPE.equals(best.getMediaType()) ||
TTL_MIMETYPE.equals(best.getMediaType()) ))
return best;
}
/*
* check for parts of URL that indicate request for RDF
http://vivo.cornell.edu/individual/n23/n23.rdf
http://vivo.cornell.edu/individual/n23/n23.n3
http://vivo.cornell.edu/individual/n23/n23.ttl
*/
Matcher m = RDF_REQUEST.matcher(url);
if( m.matches() )
return new ContentType(RDFXML_MIMETYPE);
m = N3_REQUEST.matcher(url);
if( m.matches() )
return new ContentType(N3_MIMETYPE);
m = TTL_REQUEST.matcher(url);
if( m.matches() )
return new ContentType(TTL_MIMETYPE);
} catch (Throwable th) {
log.error("problem while checking accept header " , th);
}
return null;
}
private boolean checkForSunset(VitroRequest vreq, Individual entity) {
// TODO Auto-generated method stub
return false;
}
private boolean checkForHidden(VitroRequest vreq, Individual entity){
// TODO Auto-generated method stub
return false;
}
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
Resource subj = newModel.getResource(entity.getURI());
List<DataPropertyStatement> dstates = entity.getDataPropertyStatements();
//System.out.println("data: "+dstates.size());
TypeMapper typeMapper = TypeMapper.getInstance();
for (DataPropertyStatement ds: dstates) {
Property dp = newModel.getProperty(ds.getDatapropURI());
Literal lit = null;
if ((ds.getLanguage()) != null && (ds.getLanguage().length()>0)) {
lit = newModel.createLiteral(ds.getData(),ds.getLanguage());
} else if ((ds.getDatatypeURI() != null) && (ds.getDatatypeURI().length()>0)) {
lit = newModel.createTypedLiteral(ds.getData(),typeMapper.getSafeTypeByName(ds.getDatatypeURI()));
} else {
lit = newModel.createLiteral(ds.getData());
}
newModel.add(newModel.createStatement(subj, dp, lit));
}
if( recurseDepth < 5 ){
List<ObjectPropertyStatement> ostates = entity.getObjectPropertyStatements();
for (ObjectPropertyStatement os: ostates) {
ObjectProperty objProp = os.getProperty();
Property op = newModel.getProperty(os.getPropertyURI());
Resource obj = newModel.getResource(os.getObjectURI());
newModel.add(newModel.createStatement(subj, op, obj));
if( objProp.getStubObjectRelation() )
newModel.add(getRDF(os.getObject(), contextModel, newModel, recurseDepth + 1));
}
}
newModel = getLabelAndTypes(entity, contextModel, newModel );
return newModel;
}
/* Get the properties that are difficult to get via a filtered WebappDaoFactory. */
private Model getLabelAndTypes(Individual entity, Model ontModel, Model newModel){
for( VClass vclass : entity.getVClasses()){
newModel.add(newModel.getResource(entity.getURI()), RDF.type, newModel.getResource(vclass.getURI()));
}
ontModel.enterCriticalSection(Lock.READ);
try {
newModel.add(ontModel.listStatements(ontModel.getResource(entity.getURI()), RDFS.label, (RDFNode)null));
} finally {
ontModel.leaveCriticalSection();
}
return newModel;
}
private void checkForSearch(HttpServletRequest req, Individual ent) {
if (req.getSession().getAttribute("LastQuery") != null) {
VitroQueryWrapper qWrap = (VitroQueryWrapper) req.getSession()
.getAttribute("LastQuery");
if (qWrap.getRequestCount() > 0 && qWrap.getQuery() != null) {
VitroQuery query = qWrap.getQuery();
//set query text so we can get it in JSP
req.setAttribute("querytext", query.getTerms());
//setup highlighting for output
StringProcessorTag.putStringProcessorInRequest(req, qWrap.getHighlighter());
qWrap.setRequestCount(qWrap.getRequestCount() - 1);
} else {
req.getSession().removeAttribute("LastQuery");
}
}
}
private Pattern badrequest= Pattern.compile(".*([&\\?=]|\\.\\.).*");
public String getViewFromRequest(HttpServletRequest request){
String viewParam = request.getParameter("view");
if( viewParam != null ){
if( badrequest.matcher(viewParam).matches() ){
log.debug("request for a non-default view was bad: " + viewParam);
return null;
}else{
log.debug("view request : " + viewParam);
return viewParam;
}
}
return null;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException {
doGet(request, response);
}
private void doHelp(HttpServletResponse res)
throws IOException, ServletException {
ServletOutputStream out = res.getOutputStream();
res.setContentType("text/html; charset=UTF-8");
out.println("<html><body><h2>Quick Notes on using entity:</h2>");
out.println("<p>id is the id of the entity to query for. netid also works.</p>");
out.println("</body></html>");
}
private void doNotFound(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
VitroRequest vreq = new VitroRequest(req);
Portal portal = vreq.getPortal();
ApplicationBean appBean = ApplicationBean.getAppBean(getServletContext());
int allPortalId = appBean.getAllPortalFlagNumeric();
//If an Individual is not found, there is possibility that it
//was requested from a portal where it was not visible.
//In this case redirect to the all portal.
try{
Portal allPortal =
vreq.getWebappDaoFactory().getPortalDao().getPortal(allPortalId);
// there must be a portal defined with the ID of the all portal
// for this to work
if( portal.getPortalId() != allPortalId && allPortal != null ) {
//bdc34:
// this is hard coded to get the all portal
// I didn't find a way to get the id of the all portal
// it is likely that redirecting will not work in non VIVO clones
String portalPrefix = null;
String portalParam = null;
if( allPortal != null && allPortal.getUrlprefix() != null )
portalPrefix = allPortal.getUrlprefix();
else
portalParam = "home=" + allPortalId;
String queryStr = req.getQueryString();
if( queryStr == null && portalParam != null && !"".equals(portalParam)){
queryStr = portalParam;
} else {
if( portalParam != null && !"".equals(portalParam))
queryStr = queryStr + "&" + portalParam;
}
if( queryStr != null && !queryStr.startsWith("?") )
queryStr = "?" + queryStr;
StringBuilder url = new StringBuilder();
url.append( req.getContextPath() );
if( req.getContextPath() != null && !req.getContextPath().endsWith("/"))
url.append('/');
if( portalPrefix != null && !"".equals(portalPrefix))
url.append( portalPrefix ).append('/');
String servletPath = req.getServletPath();
String spath = "";
if( servletPath != null ){
if( servletPath.startsWith("/") )
spath = servletPath.substring(1);
else
spath = servletPath;
}
if( spath != null && !"".equals(spath))
url.append( spath );
if( req.getPathInfo() != null )
url.append( req.getPathInfo() );
if( queryStr != null && !"".equals(queryStr ))
url.append( queryStr );
res.sendRedirect(url.toString());
return;
}
}catch(Throwable th){
log.error("could not do a redirect", th);
}
//set title before we do the highlighting so we don't get markup in it.
req.setAttribute("title","not found");
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
String css = "<link rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\""
+ portal.getThemeDir() + "css/entity.css\"/>"
+ "<script language='JavaScript' type='text/javascript' src='js/toggle.js'></script>";
req.setAttribute("css",css);
req.setAttribute("bodyJsp","/"+Controllers.ENTITY_NOT_FOUND_JSP);
RequestDispatcher rd = req.getRequestDispatcher(Controllers.BASIC_JSP);
rd.forward(req,res);
}
private String forURL(String frag)
{
String result = null;
try
{
result = URLEncoder.encode(frag, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException("UTF-8 not supported", ex);
}
return result;
}
private class HelpException extends Throwable{}
private class EntityNotFoundException extends Throwable{}
}

View file

@ -0,0 +1,141 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
import edu.cornell.mannlib.vitro.webapp.view.IndividualView;
public class IndividualListControllerFM extends FreeMarkerHttpServlet {
long startTime = -1;
private static final Log log = LogFactory.getLog(IndividualListControllerFM.class.getName());
private VClass vclass = null;
/**
* This generates a list of entities and then sends that
* off to a jsp to be displayed.
*
* Expected parameters:
*
* Expected Attributes:
* entity - set to entity to display properties for.
*
* @author bdc34
*/
// TODO Rewrite error cases to use FreeMarker templates. Restructure so we're always doing the body
// and then calling writeOutput().
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws IOException, ServletException {
startTime = System.currentTimeMillis(); // TODO: remove
try {
super.doSetup(req, res);
Object obj = vreq.getAttribute("vclass");
vclass=null;
if( obj == null ) { // look for vitroclass id parameter
String vitroClassIdStr=req.getParameter("vclassId");
if (vitroClassIdStr!=null && !vitroClassIdStr.equals("")) {
try {
//TODO have to change this so vclass's group and entity count are populated
vclass = vreq.getWebappDaoFactory().getVClassDao().getVClassByURI(vitroClassIdStr);
if (vclass == null) {
log.error("Couldn't retrieve vclass "+vitroClassIdStr);
response.sendRedirect(Controllers.BROWSE_CONTROLLER+"-freemarker?"+vreq.getQueryString());
}
} catch (Exception ex) {
throw new HelpException("EntityListControllerFM: request parameter 'vclassId' must be a URI string");
}
}
} else if (obj instanceof VClass) {
vclass = (VClass)obj;
} else {
throw new HelpException("EntityListControllerFM: attribute 'vclass' must be of type "
+ VClass.class.getName() );
}
if (vclass!=null){
setBody();
write(response);
}
// RY Rewrite error cases for FreeMarker, not JSP
} catch (HelpException help){
doHelp(response);
} catch (Throwable e) {
vreq.setAttribute("javax.servlet.jsp.jspException",e);
RequestDispatcher rd = req.getRequestDispatcher("/error.jsp");
rd.forward(vreq, response);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException {
doGet(request, response);
}
protected String getBody() {
Map<String, Object> body = new HashMap<String, Object>();
// Create list of individuals
List<Individual> individualList = vreq.getWebappDaoFactory().getIndividualDao().getIndividualsByVClass(vclass);
List<IndividualView> individuals = new ArrayList<IndividualView>();
body.put("individuals", individuals);
// But the JSP version includes url rewriting via URLRewritingHttpServletResponse
body.put("individualUrl", config.getSharedVariable("contextPath") + "/entity?home=" + config.getSharedVariable("portalId") + "&uri=");
if (individuals == null) {
log.error("individuals list is null");
}
// Use instead of getTitle(), because we have a subtitle too
String title = "";
VClassGroup classGroup=vclass.getGroup();
if (classGroup==null) {
title = vclass.getName();
} else {
title = classGroup.getPublicName();
setSharedVariable("subTitle", vclass.getName());
}
setSharedVariable("title", title);
String templateName = "entityList.ftl";
return mergeBodyToTemplate(templateName, body);
}
// RY Rewrite as a template
private void doHelp(HttpServletResponse res)
throws IOException, ServletException {
ServletOutputStream out = res.getOutputStream();
res.setContentType("text/html; charset=UTF-8");
out.println("<html><body><h2>Quick Notes on using EntityList:</h2>");
out.println("<p>request.attributes 'entities' must be set by servlet before calling."
+" It must be a List of Entity objects </p>");
out.println("</body></html>");
}
private class HelpException extends Throwable{
public HelpException(String string) {
super(string);
}
}
}

View file

@ -0,0 +1,78 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.servlet.setup;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
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.controller.FreeMarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.view.ViewObject;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException;
public class FreeMarkerSetup implements ServletContextListener {
private static final Log log = LogFactory.getLog(FreeMarkerSetup.class);
public void contextInitialized(ServletContextEvent event) {
ServletContext sc = event.getServletContext();
// RY Change this to multi-location template scheme
String templatePath = sc.getRealPath("/templates/freemarker");
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.
try {
cfg.setDirectoryForTemplateLoading(new File(templatePath));
} catch (IOException e) {
log.error("Error specifying template directory.");
}
// RY This setting won't take effect until we use Configuration.getTemplate() to
// create templates.
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. This is an advanced topic...
// but just use this:
cfg.setObjectWrapper(new DefaultObjectWrapper());
try {
cfg.setSetting("url_escaping_charset", "ISO-8859-1");
} catch (TemplateException e) {
// TODO Auto-generated catch block
log.error("Error setting value for url_escaping_charset.");
}
FreeMarkerHttpServlet.config = cfg;
String contextPath = sc.getContextPath();
FreeMarkerHttpServlet.contextPath = contextPath;
ViewObject.contextPath = contextPath;
}
public void contextDestroyed(ServletContextEvent event) {
// nothing to do here
}
}

View file

@ -4,7 +4,6 @@ package edu.cornell.mannlib.vitro.webapp.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class StringUtils {

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.display;
package edu.cornell.mannlib.vitro.webapp.utils.view;
import java.util.Arrays;
import java.util.List;

View file

@ -0,0 +1,54 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
public class IndividualView extends ViewObject {
private static final Log log = LogFactory.getLog(IndividualView.class.getName());
private static final String URL = "/individual";
private Individual individual;
public IndividualView(Individual individual) {
this.individual = individual;
}
public String getName() {
return individual.getName();
}
public String getMoniker() {
return individual.getMoniker();
}
public String getUri() {
return individual.getURI();
}
// Or maybe getProfileUrl - there might be other kinds of urls
// e.g., getEditUrl, getDeleteUrl - these would return the computations of PropertyEditLinks
public String getProfileUrl() {
return contextPath + URL + ""; // ADD IN the label from the individual's uri
}
public String getCustomView() {
// see code currently in entityList.ftl
String customView = null;
return customView;
}
public Object getProperty(String propertyName) {
return new Object();
}
}

View file

@ -0,0 +1,68 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
/*
* A VClassGroupDisplay object is associated with a VClassGroup for display.
* It is an object that contains a linked list, rather than a type of linked list,
* so that JSP EL can access properties such as publicName.
*
* RY We may want an abstract display class as a superclass.
* RY We may want an interface that the superclass would implement.
* RY We may want to nest this class in the VClassGroup class.
*/
public class VClassGroupView extends ViewObject {
private static final Log log = LogFactory.getLog(VClassGroupView.class.getName());
private VClassGroup vClassGroup = null;
private List<VClassView> classes = null;
public VClassGroupView(VClassGroup vClassGroup) {
this.vClassGroup = vClassGroup;
}
public int getDisplayRank() {
return vClassGroup.getDisplayRank();
}
public String getUri() {
return vClassGroup.getURI();
}
public String getNamespace() {
return vClassGroup.getNamespace();
}
public String getLocalName() {
return vClassGroup.getLocalName();
}
public String getPublicName() {
return vClassGroup.getPublicName();
}
public List<VClassView> getClasses() {
// Do we need to store the classes as an instance member? Would we ever access this method more than once per template?
if (classes == null) {
List<VClass> classList = vClassGroup.getVitroClassList();
classes = new ArrayList<VClassView>();
Iterator<VClass> i = classList.iterator();
while (i.hasNext()) {
classes.add(new VClassView((VClass) i.next()));
}
}
return classes;
}
}

View file

@ -0,0 +1,33 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
public class VClassView extends ViewObject {
private static final Log log = LogFactory.getLog(VClassView.class.getName());
private static final String URL = "/individuallistFM?vclassId=";
private VClass vclass;
public VClassView(VClass vclass) {
this.vclass = vclass;
}
public String getName() {
return vclass.getName();
}
public String getUrl() {
return contextPath + URL + encodeUrl(vclass.getURI());
}
public int getEntityCount() {
return vclass.getEntityCount();
}
}

View file

@ -0,0 +1,37 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.FreeMarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
public abstract class ViewObject {
private static final Log log = LogFactory.getLog(ViewObject.class.getName());
public static String contextPath;
protected static String getUrl(String path) {
return FreeMarkerHttpServlet.getUrl(path);
}
protected String encodeUrl(String url) {
String encoding = "ISO-8859-1";
String encodedUrl = null;
try {
encodedUrl = URLEncoder.encode(url, encoding);
} catch (UnsupportedEncodingException e) {
log.error("Error encoding url " + url + " with encoding " + encoding + ": Unsupported encoding.");
}
return encodedUrl;
}
}

View file

@ -0,0 +1,29 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view.menu;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
/** A menu that can indicate the active item.
*
* @author rjy7
*
*/
public class MainMenu extends Menu {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(MainMenu.class.getName());
public MainMenu(VitroRequest vreq, int portalId) {
super(vreq, portalId);
}
public void addItem(String text, String path) {
boolean active = vreq.getServletPath().equals(path);
MainMenuItem i = new MainMenuItem(text, path, active);
items.add(i);
}
}

View file

@ -0,0 +1,29 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view.menu;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
/** A menu item that indicates whether it is the active item or not.
*
* @author rjy7
*
*/
public class MainMenuItem extends MenuItem {
private static final Log log = LogFactory.getLog(MainMenuItem.class.getName());
private boolean active;
public MainMenuItem(String linkText, String path, boolean active) {
super(linkText, path);
this.active = active;
}
public boolean isActive() {
return active;
}
}

View file

@ -0,0 +1,37 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view.menu;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.view.ViewObject;
public class Menu extends ViewObject {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(Menu.class.getName());
protected VitroRequest vreq;
protected int portalId;
protected List<MenuItem> items;
public Menu(VitroRequest vreq, int portalId) {
this.vreq = vreq;
this.portalId = portalId;
items = new ArrayList<MenuItem>();
}
public void addItem(String text, String path) {
items.add(new MenuItem(text, path));
}
public List<MenuItem> getItems() {
return items;
}
}

View file

@ -0,0 +1,30 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view.menu;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.view.ViewObject;
public class MenuItem extends ViewObject {
private static final Log log = LogFactory.getLog(MenuItem.class.getName());
private String text;
private String url;
public MenuItem(String linkText, String path) {
text = linkText;
url = getUrl(path);
}
public String getLinkText() {
return text;
}
public String getUrl() {
return url;
}
}

View file

@ -0,0 +1,51 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.view.menu;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Tab;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.web.TabWebUtil;
/** A main menu constructed from persisted tab data
*
* @author rjy7
*
*/
public class TabMenu extends MainMenu {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(TabMenu.class.getName());
public TabMenu(VitroRequest vreq, int portalId) {
super(vreq, portalId);
//Tabs stored in database
List<Tab> primaryTabs = vreq.getWebappDaoFactory().getTabDao().getPrimaryTabs(portalId);
int tabId = TabWebUtil.getTabIdFromRequest(vreq);
int rootId = TabWebUtil.getRootTabId(vreq);
List tabLevels = vreq.getWebappDaoFactory().getTabDao().getTabHierarcy(tabId,rootId);
vreq.setAttribute("tabLevels", tabLevels);
Iterator<Tab> primaryTabIterator = primaryTabs.iterator();
Iterator tabLevelIterator = tabLevels.iterator();
Tab tab;
while (primaryTabIterator.hasNext()) {
tab = (Tab) primaryTabIterator.next();
addItem(tab.getTitle(), "/index.jsp?primary=" + tab.getTabId());
// RY Also need to loop through nested tab levels, but not doing that now.
}
// Hard-coded tabs. It's not really a good idea to have these here, since any menu item that doesn't
// come from the db should be accessible to the template to change the text. But we need them here
// to apply the "active" mechanism.
addItem("Index", "/browsecontroller");
addItem("Index - FM", "/browse");
}
}

View file

@ -0,0 +1,12 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt -->
<h2>${title}</h2>
<#if aboutText??>
<div class="pageGroupBody" id="aboutText">${aboutText}</div>
</#if>
<#if acknowledgeText??>
<div class="pageGroupBody" id="acknowledgementText">${acknowledgeText}</div>
</#if>

View file

@ -0,0 +1,14 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#if message??>
<p>${message}</p>
<#else>
<#list classGroups as classGroup>
<h2>${classGroup.publicName}</h2>
<ul>
<#list classGroup.classes as class>
<li><a href="${class.url}">${class.name}</a> (${class.entityCount})</li>
</#list>
</ul>
</#list>
</#if>

View file

@ -0,0 +1,35 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<div class="contents">
<div class="entityList">
<h2>${title}</h2>
<#if subtitle??>
<h4>${subTitle}"</h4>
</#if>
<ul>
<#list entities as entity>
<#-- Iterate through the object's class hierarchy, looking for a custom view -->
<#-- RY This should be done in a view method -->
<#list entity.VClasses as type>
<#if type.customSearchView?has_content>
<#assign altRender = type.customSearchView>
<#-- What order are these returned in? If from specific to general, we should break if we find one -->
</#if>
</#list>
<li>
<#-- RY Create a directive to work like c:url with c:param -->
<#-- RY The JSP version includes URL rewriting in a filter - see URLRewritingHttpServletResponse -->
<a href="${entityUrl}${entity.URI?url}">${entity.name}</a> | <#-- add p:process to name -->
<#if entity.moniker?has_content>
${entity.moniker} <#-- add p:process -->
<#else>
${entity.VClass.name}
</#if>
</li>
</#list>
</ul>
</div>
</div>

View file

@ -0,0 +1,12 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#if copyright??>
<div class="copyright">
&copy;${copyright.year?c}
<#if copyright.url??>
<a href="${copyright.url}">${copyright.text}</a>
<#else>
${copyright.text}
</#if>
</div>
</#if>

View file

@ -0,0 +1,5 @@
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

View file

@ -0,0 +1,28 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#import "/macros/list.ftl" as l>
<div id="footer">
<#if bannerImageUrl??>
<img class="footerLogo" src="${urls.bannerImage}" alt="${tagline!}" />
</#if>
<div class="footerLinks">
<ul class="otherNav">
<@l.makeList>
<li><a href="${urls.about}" title="more about this web site">About</a></li>,
<#if urls.contact??>
<li><a href="${urls.contact}" title="feedback form">Contact Us</a></li>
</#if>
</@l.makeList>
</ul>
</div>
<#include "copyright.ftl">
All Rights Reserved. <a href="${urls.termsOfUse}">Terms of Use</a>
</div>
<#-- SCRIPT TAGS SHOULD GO HERE -->

View file

@ -0,0 +1,6 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<head>
<#include "title.ftl">
<#include "stylesheets.ftl">
</head>

View file

@ -0,0 +1,33 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#import "/macros/list.ftl" as l>
<div id="identity">
<h1><a title="Home" href="${urls.home}">${siteName}</a></h1>
<#-- RY We will need this in non-NIHVIVO versions
<#if tagline.has_content>
<em>${tagline}</em>
</#if>
-->
<ul id="otherMenu">
<@l.makeList>
<#if loginName??>
<li>
Logged in as <strong>${loginName}</strong> (<a href="${urls.logout}">Log out</a>)
</li>,
<li><a href="${urls.siteAdmin}">Site Admin</a></li>,
<#else>
<li><a title="log in to manage this site" href="${urls.login}">Log in</a></li>,
</#if>
<li><a href="${urls.about}$">About</a></li>,
<li><a href="${urls.aboutFM}">About - FM</a></li>,
<#if urls.contact??>
<li><a href="${urls.contact}">Contact Us</a></li>
</#if>
</@l.makeList>
</ul>
</div>

View file

@ -0,0 +1,13 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<div id="primaryAndOther">
<ul id="primary">
<#list tabMenu.items as item>
<li>
<a href="${item.url}" <#if item.active> class="activeTab" </#if>>
${item.linkText}
</a>
</li>
</#list>
</ul>
</div>

View file

@ -0,0 +1 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->

View file

@ -0,0 +1,19 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<div id="searchBlock">
<form id="searchForm" action="${urls.search}" >
<label for="search">Search </label>
<#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>
</select>
<#else>
<input type="hidden" name="flag1" value="${portalId}" />
</#if>
<input type="text" name="querytext" id="search" class="search-form-item" value="${querytext!}" size="20" />
<input class="search-form-submit" name="submit" type="submit" value="Search" />
</form>
</div>

View file

@ -0,0 +1,8 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<link rel="stylesheet" type="text/css" href="${stylesheetDir}screen.css" media="screen"/>
<link rel="stylesheet" type="text/css" href="${stylesheetDir}print.css" media="print"/>
<#list stylesheets as stylesheet>
${stylesheet}
</#list>

View file

@ -0,0 +1,3 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<title>${title}</title>

View file

@ -0,0 +1,68 @@
<#--
Output a sequence of <li> elements, adding classes "first" and "last" to first and last list elements, respectively.
It is helpful when the list elements are generated conditionally, to avoid complex tests for the presence/absence
of other list elements in order to assign these classes.
Input should be a series of <li> elements separated by some delimiter. Default delimiter value is ",".
Tolerates a delimiter following the last <li> element.
Usage:
<@makeList>
<li>apples</li>,
<li>bananas</li>,
<li>oranges</li>
<@makeList>
<@makeList delim="??">
<li>apples, oranges</li>??
<li>bananas, lemons</li>??
<li>grapefruit, limes</li>
<@makeList>
RY Consider rewriting in Java. Probably designers won't want to modify this.
-->
<#macro makeList delim=",">
<#assign text>
<#nested>
</#assign>
<#-- Strip out a list-final delimiter, else (unlike most languages) it results in an empty final array item. -->
<#assign text = text?replace("${delim}$", "", "r")>
<#assign items = text?split(delim)>
<#list items as item>
<#-- A FreeMarker loop variable cannot have its value modified, so we use a new variable. -->
<#assign newItem = item?trim>
<#assign classVal = "">
<#-- Keep any class value already assigned -->
<#assign currentClass = newItem?matches("^<li [^>]*(class=[\'\"](.*?)[\'\"])")>
<#list currentClass as m>
<#assign classVal = m?groups[2]>
<#assign newItem = newItem?replace(m?groups[1], "")>
</#list>
<#-- Test indices, rather than comparing content, on the remote chance
that there are two list items with the same content. -->
<#-- <#if item == arr?first> -->
<#if item_index == 0>
<#assign classVal = "${classVal} first">
</#if>
<#-- <#if item == arr?last> -->
<#if !item_has_next>
<#assign classVal = "${classVal} last">
</#if>
<#if classVal != "">
<#assign classVal = classVal?replace("^ ", "", "r")>
<#-- Replace first instance only, in case the item contains nested li tags. -->
<#assign newItem = newItem?replace("<li", "<li class=\"${classVal}\"", "f")>
</#if>
${newItem}
</#list>
</#macro>
<#----------------------------------------------------------------------------->

View file

@ -0,0 +1,32 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#include "/components/doctype.html">
<#include "/components/head.ftl">
<body>
<div id="wrap" class="container">
<div id="header">
<#include "/components/identity.ftl">
<div id="navAndSearch" class="block">
<#include "/components/menu.ftl">
<#include "/components/search.ftl">
</div> <!-- navAndSearch -->
</div> <!-- header -->
<hr class="hidden" />
<div id="contentwrap">
<div id="content" <#if contentClass??> class="${contentClass}" </#if>>
${body}
</div> <!-- content -->
</div> <!-- contentwrap -->
<#include "/components/footer.ftl">
</div> <!-- wrap -->
</body>
</html>