NIHVIVO-2411 Create a mock implementation of the ShortViewService and incorporate it into the search page. Because this is a mock, it always uses the default short view, like before. There should be no difference for the end-user.

This commit is contained in:
j2blake 2012-04-25 19:32:50 +00:00
parent f2707b0bb1
commit 022506cbf4
11 changed files with 529 additions and 24 deletions

View file

@ -19,6 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean; import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants;
import edu.cornell.mannlib.vitro.webapp.web.directives.IndividualShortViewDirective;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod; import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod; import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualProfileUrlMethod; import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualProfileUrlMethod;
@ -154,6 +155,7 @@ public class FreemarkerConfiguration extends Configuration {
map.put("dump", new freemarker.ext.dump.DumpDirective()); map.put("dump", new freemarker.ext.dump.DumpDirective());
map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective()); map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective());
map.put("help", new freemarker.ext.dump.HelpDirective()); map.put("help", new freemarker.ext.dump.HelpDirective());
map.put("shortView", new IndividualShortViewDirective());
return map; return map;
} }

View file

@ -0,0 +1,74 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.shortview;
import static edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext.SEARCH;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
/**
* TODO
*
* Get rid of this when the Application Ontology is implemented.
*/
public class FakeApplicationOntologyService {
private static final String FACULTY_MEMBER_CLASS_URI = "http://vivoweb.org/ontology/core#FacultyMember";
/**
* Return the template name and DataGetter instances associated with this
* class and this short view context. If none, return null.
*/
public TemplateAndDataGetters getShortViewProperties(String classUri,
String contextName) {
// if ((SEARCH.name().equals(contextName))
// && (classUri.equals(FACULTY_MEMBER_CLASS_URI))) {
// return new TemplateAndDataGetters("view-search-faculty.ftl", new FakeFacultyDataGetter());
// } else {
return null;
// }
}
/** The info associated with a short view. */
public static class TemplateAndDataGetters {
private final String templateName;
private final Set<DataGetter> dataGetters;
public TemplateAndDataGetters(String templateName,
DataGetter... dataGetters) {
this.templateName = templateName;
this.dataGetters = new HashSet<DataGetter>(
Arrays.asList(dataGetters));
}
public String getTemplateName() {
return templateName;
}
public Set<DataGetter> getDataGetters() {
return dataGetters;
}
}
private static class FakeFacultyDataGetter implements DataGetter {
@Override
public Map<String, Object> getData(ServletContext context,
VitroRequest vreq, Map<String, Object> valueMap) {
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> extras = new HashMap<String, Object>();
extras.put("departmentName", "Department of Redundancy Department");
map.put("extra", extras);
return map;
}
}
}

View file

@ -0,0 +1,96 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.shortview;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
/**
* Define a service that will produce HTML snippets for short views on
* Individuals.
*/
public interface ShortViewService {
/**
* Render the short view template that applies to this individual in this
* context. The data in the modelMap can be used to populate the template,
* along with any additional data returned by custom data getters.
*
* If there are any problems, return a dummy piece of text that includes the
* label of the individual. Never return null or empty string.
*
* This method should not be called from within an ongoing Freemarker
* process. In that case, use getShortViewInfo() instead.
*/
String renderShortView(Individual individual, ShortViewContext context,
Map<String, Object> modelMap, VitroRequest vreq);
/**
* What template should be used to render the short view of this individual
* in this context? What data is available from custom data getters?
*
* Ask the Application Ontology for short view specifications on each of the
* most specific classes for this individual. If more than one such class
* has an applicable short view, the class with with the first URI
* (alphabetically) will be used.
*/
TemplateAndSupplementalData getShortViewInfo(Individual individual,
ShortViewContext svContext, VitroRequest vreq);
/**
* The information associated with a particular short view.
*/
public interface TemplateAndSupplementalData {
/**
* The name of the template to be used in the short view.
*
* Either the custom view assigned to the individual and context, or the
* default view. Never empty or null, but it might refer to a template
* that can't be located.
*/
String getTemplateName();
/**
* The results of any custom data getters were associated with this
* individual in this short view context.
*
* May be empty, but never null.
*/
Map<String, Object> getSupplementalData();
}
/**
* The available contexts for short views.
*/
public enum ShortViewContext {
SEARCH("view-search-default.ftl");
private final String defaultTemplateName;
ShortViewContext(String defaultTemplateName) {
this.defaultTemplateName = defaultTemplateName;
}
public String getDefaultTemplateName() {
return defaultTemplateName;
}
public static ShortViewContext fromString(String string) {
for (ShortViewContext c : ShortViewContext.values()) {
if (c.name().equalsIgnoreCase(string)) {
return c;
}
}
return null;
}
public static String valueList() {
return StringUtils.join(ShortViewContext.values(), ", ");
}
}
}

View file

@ -0,0 +1,121 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.shortview;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.servlet.ServletContext;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters;
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
/**
* The basic implementation of ShortViewService
*/
public class ShortViewServiceImpl implements ShortViewService {
private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap();
private final FakeApplicationOntologyService faker;
public ShortViewServiceImpl(FakeApplicationOntologyService faker) {
this.faker = faker;
}
@Override
public String renderShortView(Individual individual,
ShortViewContext context, Map<String, Object> modelMap,
VitroRequest vreq) {
TemplateAndSupplementalData tsd = getShortViewInfo(individual, context,
vreq);
// TODO Auto-generated method stub
throw new RuntimeException(
"ShortViewService.renderShortView() not implemented.");
}
@Override
public TemplateAndSupplementalData getShortViewInfo(Individual individual,
ShortViewContext svContext, VitroRequest vreq) {
TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual,
svContext);
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), vreq);
return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(),
gotData);
}
/** Get most specific classes from Individual, sorted by alpha. */
private SortedSet<String> figureMostSpecificClassUris(Individual individual) {
SortedSet<String> classUris = new TreeSet<String>();
List<ObjectPropertyStatement> stmts = individual
.getObjectPropertyStatements(VitroVocabulary.MOST_SPECIFIC_TYPE);
for (ObjectPropertyStatement stmt : stmts) {
classUris.add(stmt.getObjectURI());
}
return classUris;
}
/** Find the template and data getters for this individual in this context. */
private TemplateAndDataGetters fetchTemplateAndDataGetters(
Individual individual, ShortViewContext svContext) {
List<String> classUris = new ArrayList<String>();
classUris.addAll(figureMostSpecificClassUris(individual));
for (String classUri : classUris) {
TemplateAndDataGetters tdg = faker.getShortViewProperties(classUri,
svContext.name());
if (tdg != null) {
return tdg;
}
}
// Didn't find one? Use the default values.
return new TemplateAndDataGetters(svContext.getDefaultTemplateName());
}
/** Build a data map from the combined results of all data getters. */
private Map<String, Object> runDataGetters(Set<DataGetter> dataGetters,
VitroRequest vreq) {
ServletContext ctx = vreq.getSession().getServletContext();
Map<String, Object> gotData = new HashMap<String, Object>();
for (DataGetter dg : dataGetters) {
gotData.putAll(dg.getData(ctx, vreq, EMPTY_MAP));
}
return gotData;
}
private static class TemplateAndSupplementalDataImpl implements
TemplateAndSupplementalData {
private final String templateName;
private final Map<String, Object> customData;
public TemplateAndSupplementalDataImpl(String templateName,
Map<String, Object> customData) {
this.templateName = templateName;
this.customData = customData;
}
@Override
public String getTemplateName() {
return templateName;
}
@Override
public Map<String, Object> getSupplementalData() {
return customData;
}
}
}

View file

@ -0,0 +1,39 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.shortview;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
/**
* Set up the ShortViewService.
*/
public class ShortViewServiceSetup implements ServletContextListener {
private static final String ATTRIBUTE_NAME = ShortViewService.class
.getName();
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
StartupStatus ss = StartupStatus.getBean(ctx);
ShortViewServiceImpl svs = new ShortViewServiceImpl(
new FakeApplicationOntologyService());
ctx.setAttribute(ATTRIBUTE_NAME, svs);
ss.info(this,
"Started the Short View Service with a ShortViewServiceImpl");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
sce.getServletContext().removeAttribute(ATTRIBUTE_NAME);
}
public static ShortViewService getService(ServletContext ctx) {
return (ShortViewService) ctx.getAttribute(ATTRIBUTE_NAME);
}
}

View file

@ -4,19 +4,17 @@ package edu.cornell.mannlib.vitro.webapp.web.directives;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.SimpleScalar;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelException;
import freemarker.template.utility.DeepUnwrap;
public abstract class BaseTemplateDirectiveModel implements TemplateDirectiveModel { public abstract class BaseTemplateDirectiveModel implements TemplateDirectiveModel {
@ -48,4 +46,41 @@ public abstract class BaseTemplateDirectiveModel implements TemplateDirectiveMod
return template; return template;
} }
// ----------------------------------------------------------------------
// Convenience methods for parsing the parameter map
// ----------------------------------------------------------------------
/** Get the parameter, or throw an exception. */
protected String getRequiredSimpleScalarParameter(Map<?, ?> params,
String name) throws TemplateModelException {
Object o = params.get(name);
if (o == null) {
throw new TemplateModelException("The '" + name
+ "' parameter is required" + ".");
}
if (!(o instanceof SimpleScalar)) {
throw new TemplateModelException("The '" + name
+ "' parameter must be a string value.");
}
return o.toString();
}
/** Get the parameter, or "null" if the parameter is not provided. */
protected String getOptionalSimpleScalarParameter(Map<?, ?> params,
String name) throws TemplateModelException {
Object o = params.get(name);
if (o == null) {
return null;
}
if (!(o instanceof SimpleScalar)) {
throw new TemplateModelException("The '" + name
+ "' parameter must be a string value.");
}
return o.toString();
}
} }

View file

@ -14,7 +14,6 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import freemarker.template.TemplateModel; import freemarker.template.TemplateModel;
@ -60,21 +59,6 @@ public class EmailDirective extends BaseTemplateDirectiveModel {
} }
} }
private String getOptionalSimpleScalarParameter(Map<?, ?> params,
String name) throws TemplateModelException {
Object o = params.get(name);
if (o == null) {
return null;
}
if (!(o instanceof SimpleScalar)) {
throw new TemplateModelException("The '" + name + "' parameter "
+ "for the email directive must be a string value.");
}
return o.toString();
}
@Override @Override
public Map<String, Object> help(String name) { public Map<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>(); Map<String, Object> map = new LinkedHashMap<String, Object>();

View file

@ -0,0 +1,152 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.web.directives;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
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.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewServiceSetup;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.TemplateAndSupplementalData;
import freemarker.core.Environment;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
* Find the short-view template for the specified Individual in the specified
* context. Get any required data, and render the template to HTML.
*/
public class IndividualShortViewDirective extends BaseTemplateDirectiveModel {
private static final Log log = LogFactory
.getLog(IndividualShortViewDirective.class);
@Override
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
// Get the Individual URI and check it.
String individualUri = getRequiredSimpleScalarParameter(params, "uri");
Individual individual = getIndividual(individualUri);
if (individual == null) {
throw new TemplateModelException(
"Can't find individual for URI: \"" + individualUri + "\"");
}
// Get the view context and check it.
String vcString = getRequiredSimpleScalarParameter(params,
"viewContext");
ShortViewContext viewContext = ShortViewContext.fromString(vcString);
if (viewContext == null) {
throw new TemplateModelException(
"viewContext must be one of these: "
+ ShortViewContext.valueList());
}
// Find the details of the short view and include it in the output.
renderShortView(individual, viewContext);
}
private Individual getIndividual(String individualUri) {
Environment env = Environment.getCurrentEnvironment();
HttpServletRequest request = (HttpServletRequest) env
.getCustomAttribute("request");
VitroRequest vreq = new VitroRequest(request);
WebappDaoFactory wdf = vreq.getWebappDaoFactory();
IndividualDao iDao = wdf.getIndividualDao();
return iDao.getIndividualByURI(individualUri);
}
private void renderShortView(Individual individual,
ShortViewContext svContext) {
Environment env = Environment.getCurrentEnvironment();
ServletContext ctx = (ServletContext) env.getCustomAttribute("context");
VitroRequest vreq = new VitroRequest(
(HttpServletRequest) env.getCustomAttribute("request"));
ShortViewService svs = ShortViewServiceSetup.getService(ctx);
TemplateAndSupplementalData svInfo = svs.getShortViewInfo(individual,
svContext, vreq);
ObjectWrapper objectWrapper = env.getConfiguration().getObjectWrapper();
for (String name : svInfo.getSupplementalData().keySet()) {
Object value = svInfo.getSupplementalData().get(name);
try {
env.setVariable(name, objectWrapper.wrap(value));
} catch (TemplateModelException e) {
log.error("Failed to wrap supplemental data '" + name + "' = '"
+ value + "'", e);
}
}
try {
Template template = env.getTemplateForInclusion(
svInfo.getTemplateName(), null, true);
env.include(template);
} catch (IOException e) {
log.error("Could not load template '" + svInfo.getTemplateName()
+ "'", e);
renderErrorMessage(individual);
} catch (TemplateException e) {
log.error("Could not process template '" + svInfo.getTemplateName()
+ "'", e);
renderErrorMessage(individual);
}
}
/** If there is a problem rendering the custom view, do this instead. */
private void renderErrorMessage(Individual individual) {
Environment env = Environment.getCurrentEnvironment();
try {
env.getOut().append(
"<span>Can't process the custom short view for "
+ individual.getName() + "</span>");
} catch (IOException e1) {
e1.printStackTrace();
}
}
@Override
public Map<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("effect", "Find the short view that applies "
+ "to this individual in this context -- a template and "
+ "optional DataGetters. "
+ "Execute the DataGetters and render the template.");
map.put("comments",
"The path should be an absolute path, starting with \"/\".");
Map<String, String> params = new HashMap<String, String>();
params.put("uri", "The URI of the individual being displayed.");
params.put("viewContext",
"One of these: " + ShortViewContext.valueList());
map.put("parameters", params);
List<String> examples = new ArrayList<String>();
examples.add("&lt;img src=\"<@shortView uri=individual.uri viewContext=\"SEARCH\" />\" /&gt;");
map.put("examples", examples);
return map;
}
}

View file

@ -45,6 +45,10 @@ public abstract class BaseIndividualSearchResult extends BaseTemplateModel {
/* Template properties */ /* Template properties */
public String getUri() {
return individual.getURI();
}
public String getProfileUrl() { public String getProfileUrl() {
return UrlBuilder.getIndividualProfileUrl(individual, vreq); return UrlBuilder.getIndividualProfileUrl(individual, vreq);
} }
@ -59,10 +63,6 @@ public abstract class BaseIndividualSearchResult extends BaseTemplateModel {
return types.values(); return types.values();
} }
public String getSearchView() {
return getView(ClassView.SEARCH);
}
public String getSnippet() { public String getSnippet() {
return individual.getSearchSnippet(); return individual.getSearchSnippet();
} }

View file

@ -48,6 +48,8 @@ edu.cornell.mannlib.vitro.webapp.auth.policy.RootUserPolicy$Setup
edu.cornell.mannlib.vitro.webapp.auth.policy.RestrictHomeMenuItemEditingPolicy$Setup edu.cornell.mannlib.vitro.webapp.auth.policy.RestrictHomeMenuItemEditingPolicy$Setup
edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewServiceSetup
# The Solr index uses a "public" permission, so the PropertyRestrictionPolicyHelper # The Solr index uses a "public" permission, so the PropertyRestrictionPolicyHelper
# and the PermissionRegistry must already be set up. # and the PermissionRegistry must already be set up.
edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup

View file

@ -43,7 +43,7 @@
<ul class="searchhits"> <ul class="searchhits">
<#list individuals as individual> <#list individuals as individual>
<li> <li>
<#include "${individual.searchView}"> <@shortView uri=individual.uri viewContext="search" />
</li> </li>
</#list> </#list>
</ul> </ul>