NIHVIVO-2411 Implement the BROWSE short view, which uses AJAX calls and so requires a FreemarkerProcessingService to render the view into HTML.
This commit is contained in:
parent
cf94490842
commit
47b3565e58
7 changed files with 322 additions and 10 deletions
|
@ -0,0 +1,79 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.controller.json;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewServiceSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does a Solr search for individuals, and uses the short view to render each of
|
||||||
|
* the results.
|
||||||
|
*/
|
||||||
|
public class GetRenderedSolrIndividualsByVClass extends JsonObjectProducer {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(GetRenderedSolrIndividualsByVClass.class);
|
||||||
|
|
||||||
|
protected GetRenderedSolrIndividualsByVClass(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for individuals by VClass. The class URI and the paging
|
||||||
|
* information are in the request parameters.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected JSONObject process() throws Exception {
|
||||||
|
JSONObject rObj = null;
|
||||||
|
VClass vclass = getVclassParameter(vreq);
|
||||||
|
String vclassId = vclass.getURI();
|
||||||
|
|
||||||
|
vreq.setAttribute("displayType", vclassId);
|
||||||
|
rObj = JsonServlet.getSolrIndividualsByVClass(vclassId, vreq, ctx);
|
||||||
|
addShortViewRenderings(rObj);
|
||||||
|
|
||||||
|
return rObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look through the return object. For each individual, render the short
|
||||||
|
* view and insert the resulting HTML into the object.
|
||||||
|
*/
|
||||||
|
private void addShortViewRenderings(JSONObject rObj) throws JSONException {
|
||||||
|
JSONArray individuals = rObj.getJSONArray("individuals");
|
||||||
|
String vclassName = rObj.getJSONObject("vclass").getString("name");
|
||||||
|
for (int i = 0; i < individuals.length(); i++) {
|
||||||
|
JSONObject individual = individuals.getJSONObject(i);
|
||||||
|
individual.put("shortViewHtml",
|
||||||
|
renderShortView(individual.getString("URI"), vclassName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String renderShortView(String individualUri, String vclassName) {
|
||||||
|
IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
|
||||||
|
Individual individual = iDao.getIndividualByURI(individualUri);
|
||||||
|
|
||||||
|
Map<String, Object> modelMap = new HashMap<String, Object>();
|
||||||
|
modelMap.put("individual",
|
||||||
|
new IndividualTemplateModel(individual, vreq));
|
||||||
|
modelMap.put("vclass", vclassName);
|
||||||
|
|
||||||
|
ShortViewService svs = ShortViewServiceSetup.getService(ctx);
|
||||||
|
return svs.renderShortView(individual, ShortViewContext.BROWSE,
|
||||||
|
modelMap, vreq);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import edu.cornell.mannlib.vitro.webapp.utils.pageDataGetter.PageDataGetterUtils
|
||||||
* It could be generalized to get other types of data ex. XML, HTML etc
|
* It could be generalized to get other types of data ex. XML, HTML etc
|
||||||
* @author bdc34
|
* @author bdc34
|
||||||
*
|
*
|
||||||
|
* Moved most of the logic into a group of JsonProducer classes. jeb228
|
||||||
*/
|
*/
|
||||||
public class JsonServlet extends VitroHttpServlet {
|
public class JsonServlet extends VitroHttpServlet {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@ -69,8 +70,8 @@ public class JsonServlet extends VitroHttpServlet {
|
||||||
new GetSolrIndividualsByVClasses(vreq).process(resp);
|
new GetSolrIndividualsByVClasses(vreq).process(resp);
|
||||||
} else if( vreq.getParameter("getDataForPage") != null ){
|
} else if( vreq.getParameter("getDataForPage") != null ){
|
||||||
new GetDataForPage(vreq).process(resp);
|
new GetDataForPage(vreq).process(resp);
|
||||||
// }else if( vreq.getParameter("getRenderedSolrIndividualsByVClass") != null ){
|
}else if( vreq.getParameter("getRenderedSolrIndividualsByVClass") != null ){
|
||||||
// new GetRenderedSolrIndividualsByVClass(vreq).process(resp);
|
new GetRenderedSolrIndividualsByVClass(vreq).process(resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the ability to process a Freemarker template outside of the context
|
||||||
|
* of a FreemarkerHttpServlet.
|
||||||
|
*
|
||||||
|
* The most likely place to use this is when rendering a short view that was
|
||||||
|
* invoked through an AJAX call.
|
||||||
|
*/
|
||||||
|
public interface FreemarkerProcessingService {
|
||||||
|
/**
|
||||||
|
* Is there an accessible template by this name?
|
||||||
|
*
|
||||||
|
* The question is asked in the context of the current request, which
|
||||||
|
* determines the theme directory.
|
||||||
|
*
|
||||||
|
* @throws TemplateProcessingException
|
||||||
|
* If the template is found, but cannot be parsed.
|
||||||
|
*/
|
||||||
|
boolean isTemplateAvailable(String templateName, HttpServletRequest req)
|
||||||
|
throws TemplateProcessingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a Freemarker template with a data map, producing string of HTML.
|
||||||
|
*
|
||||||
|
* This is done in the context of the current HttpServletRequest, which
|
||||||
|
* provides a wide range of ancillary information, including (but not
|
||||||
|
* limited to) theme directory, context path, info on logged-in user,
|
||||||
|
* authorizations for the current user, etc., etc.
|
||||||
|
*/
|
||||||
|
String renderTemplate(String templateName, Map<String, Object> map,
|
||||||
|
HttpServletRequest req) throws TemplateProcessingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a failure to render the given template with the given data.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class TemplateProcessingException extends Exception {
|
||||||
|
|
||||||
|
public TemplateProcessingException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateProcessingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateProcessingException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateProcessingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template parser detected a problem.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class TemplateParsingException extends
|
||||||
|
TemplateProcessingException {
|
||||||
|
|
||||||
|
public TemplateParsingException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateParsingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateParsingException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateParsingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
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.controller.freemarker.FreemarkerConfiguration;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerConfigurationLoader;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.log.LogUtils;
|
||||||
|
import freemarker.core.Environment;
|
||||||
|
import freemarker.core.ParseException;
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.Template;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the FreemarkerProcessingService.
|
||||||
|
*/
|
||||||
|
public class FreemarkerProcessingServiceImpl implements
|
||||||
|
FreemarkerProcessingService {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(FreemarkerProcessingServiceImpl.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTemplateAvailable(String templateName,
|
||||||
|
HttpServletRequest req) throws TemplateProcessingException {
|
||||||
|
return null != getTemplate(templateName, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderTemplate(String templateName, Map<String, Object> map,
|
||||||
|
HttpServletRequest req) throws TemplateProcessingException {
|
||||||
|
log.debug("renderTemplate: '" + templateName + "' with "
|
||||||
|
+ LogUtils.deepFormatForLog(log, "debug", map));
|
||||||
|
Template template = getTemplate(templateName, req);
|
||||||
|
return processTemplate(template, map, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch this template from a file and parse it. If there are any problems,
|
||||||
|
* return "null".
|
||||||
|
*/
|
||||||
|
private Template getTemplate(String templateName, HttpServletRequest req)
|
||||||
|
throws TemplateProcessingException {
|
||||||
|
Template template = null;
|
||||||
|
try {
|
||||||
|
Configuration config = FreemarkerConfigurationLoader
|
||||||
|
.getConfig(new VitroRequest(req));
|
||||||
|
template = config.getTemplate(templateName);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
log.warn("Failed to parse the template at '" + templateName + "'"
|
||||||
|
+ e);
|
||||||
|
throw new TemplateParsingException(e);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log.debug("No template found for '" + templateName + "'");
|
||||||
|
throw new TemplateProcessingException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to read the template at '" + templateName + "'", e);
|
||||||
|
throw new TemplateProcessingException(e);
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processTemplate(Template template, Map<String, Object> map,
|
||||||
|
HttpServletRequest req) throws TemplateProcessingException {
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
try {
|
||||||
|
// Add directives to the map. For some reason, having them in the
|
||||||
|
// configuration is not enough.
|
||||||
|
map.putAll(FreemarkerConfiguration.getDirectives());
|
||||||
|
|
||||||
|
// Add request and servlet context as custom attributes of the
|
||||||
|
// environment, so they
|
||||||
|
// can be used in directives.
|
||||||
|
Environment env = template.createProcessingEnvironment(map, writer);
|
||||||
|
env.setCustomAttribute("request", req);
|
||||||
|
env.setCustomAttribute("context", req.getSession()
|
||||||
|
.getServletContext());
|
||||||
|
env.process();
|
||||||
|
return writer.toString();
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
throw new TemplateProcessingException(
|
||||||
|
"TemplateException creating processing environment", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new TemplateProcessingException(
|
||||||
|
"IOException creating processing environment", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This would be used to create and publish such a service, but for now we can
|
||||||
|
* simply create a fresh copy every time someone asks for one.
|
||||||
|
*/
|
||||||
|
public class FreemarkerProcessingServiceSetup {
|
||||||
|
public static FreemarkerProcessingService getService(ServletContext ctx) {
|
||||||
|
return new FreemarkerProcessingServiceImpl();
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,7 +67,8 @@ public interface ShortViewService {
|
||||||
* The available contexts for short views.
|
* The available contexts for short views.
|
||||||
*/
|
*/
|
||||||
public enum ShortViewContext {
|
public enum ShortViewContext {
|
||||||
SEARCH("view-search-default.ftl"), INDEX("listedIndividual.ftl");
|
SEARCH("view-search-default.ftl"), INDEX("listedIndividual.ftl"), BROWSE(
|
||||||
|
"view-browse-default.ftl");
|
||||||
|
|
||||||
private final String defaultTemplateName;
|
private final String defaultTemplateName;
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,16 @@ import java.util.TreeSet;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
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.Individual;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingService.TemplateParsingException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingServiceSetup;
|
||||||
import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters;
|
import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
|
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
|
||||||
|
|
||||||
|
@ -24,6 +30,9 @@ import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
|
||||||
* The basic implementation of ShortViewService
|
* The basic implementation of ShortViewService
|
||||||
*/
|
*/
|
||||||
public class ShortViewServiceImpl implements ShortViewService {
|
public class ShortViewServiceImpl implements ShortViewService {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(ShortViewServiceImpl.class);
|
||||||
|
|
||||||
private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap();
|
private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap();
|
||||||
|
|
||||||
private final FakeApplicationOntologyService faker;
|
private final FakeApplicationOntologyService faker;
|
||||||
|
@ -39,17 +48,38 @@ public class ShortViewServiceImpl implements ShortViewService {
|
||||||
|
|
||||||
TemplateAndSupplementalData tsd = getShortViewInfo(individual, context,
|
TemplateAndSupplementalData tsd = getShortViewInfo(individual, context,
|
||||||
vreq);
|
vreq);
|
||||||
|
String templateName = tsd.getTemplateName();
|
||||||
|
Map<String, Object> supplementalData = tsd.getSupplementalData();
|
||||||
|
|
||||||
// TODO Auto-generated method stub
|
try {
|
||||||
throw new RuntimeException(
|
Map<String, Object> fullModelMap = new HashMap<String, Object>(
|
||||||
"ShortViewService.renderShortView() not implemented.");
|
modelMap);
|
||||||
|
fullModelMap.putAll(supplementalData);
|
||||||
|
|
||||||
|
FreemarkerProcessingService fps = FreemarkerProcessingServiceSetup
|
||||||
|
.getService(vreq.getSession().getServletContext());
|
||||||
|
|
||||||
|
if (!fps.isTemplateAvailable(templateName, vreq)) {
|
||||||
|
return "<p>Can't locate short view template '" + templateName
|
||||||
|
+ "' for " + individual.getName() + "</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return fps.renderTemplate(templateName, fullModelMap, vreq);
|
||||||
|
} catch (TemplateParsingException e) {
|
||||||
|
return "<p>Can't parse the short view template '" + templateName
|
||||||
|
+ "' for " + individual.getName() + "</p>";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e, e);
|
||||||
|
return "<p>Failed to render the short view for "
|
||||||
|
+ individual.getName() + "</p>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TemplateAndSupplementalData getShortViewInfo(Individual individual,
|
public TemplateAndSupplementalData getShortViewInfo(Individual individual,
|
||||||
ShortViewContext svContext, VitroRequest vreq) {
|
ShortViewContext svContext, VitroRequest vreq) {
|
||||||
TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual,
|
TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual,
|
||||||
svContext);
|
svContext, vreq);
|
||||||
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), vreq);
|
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), vreq);
|
||||||
return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(),
|
return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(),
|
||||||
gotData);
|
gotData);
|
||||||
|
@ -68,18 +98,19 @@ public class ShortViewServiceImpl implements ShortViewService {
|
||||||
|
|
||||||
/** Find the template and data getters for this individual in this context. */
|
/** Find the template and data getters for this individual in this context. */
|
||||||
private TemplateAndDataGetters fetchTemplateAndDataGetters(
|
private TemplateAndDataGetters fetchTemplateAndDataGetters(
|
||||||
Individual individual, ShortViewContext svContext) {
|
Individual individual, ShortViewContext svContext, VitroRequest vreq) {
|
||||||
List<String> classUris = new ArrayList<String>();
|
List<String> classUris = new ArrayList<String>();
|
||||||
classUris.addAll(figureMostSpecificClassUris(individual));
|
classUris.addAll(figureMostSpecificClassUris(individual));
|
||||||
|
|
||||||
for (String classUri : classUris) {
|
for (String classUri : classUris) {
|
||||||
TemplateAndDataGetters tdg = faker.getShortViewProperties(classUri,
|
TemplateAndDataGetters tdg = faker.getShortViewProperties(
|
||||||
|
vreq.getWebappDaoFactory(), individual, classUri,
|
||||||
svContext.name());
|
svContext.name());
|
||||||
if (tdg != null) {
|
if (tdg != null) {
|
||||||
return tdg;
|
return tdg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't find one? Use the default values.
|
// Didn't find one? Use the default values.
|
||||||
return new TemplateAndDataGetters(svContext.getDefaultTemplateName());
|
return new TemplateAndDataGetters(svContext.getDefaultTemplateName());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue