Merge branch 'maint-rel-1.6' into develop

This commit is contained in:
brianjlowe 2013-11-25 10:45:10 -05:00
commit d8769df306
83 changed files with 2417 additions and 716 deletions

View file

@ -745,6 +745,21 @@
</td>
</tr>
<tr>
<td colspan="2">
Show only the most appropriate data values based on the Accept-Language
header supplied by the browser. Default is false if not set.
</td>
</tr>
<tr class="odd_row">
<td>
RDFService.languageFilter
</td>
<td>
false
</td>
</tr>
<tr>
<td colspan="2">
Force VIVO to use a specific language or Locale instead of those
@ -780,65 +795,6 @@
</td>
</tr>
<tr>
<td colspan="2">
<b>For developers only.</b>
Defeat the Freemarker template cache, so each template
is read from disk on each request. This permits developers to immediately
see the effect of changes to the template. The default is <code>false</code>, which
means that a cached copy of each template will be used for 60 seconds
before the disk is checked for a new version.
<br/><b>Setting this option to "true" slows down Vitro performance.</b>
</td>
</tr>
<tr class="odd_row">
<td>
developer.defeatFreemarkerCache
</td>
<td>
false
</td>
</tr>
<tr>
<td colspan="2">
<b>For developers only.</b>
Defeat the cache of language-specific text strings,
so the language file is read from disk on each request.
This permits developers to immediately
see the effect of changes to the text strings.
The default is <code>false</code>, which means that the language file is
read when VIVO starts up, or when a new theme is selected.
<br/><b>Setting this option to "true" slows down Vitro performance.</b>
</td>
</tr>
<tr class="odd_row">
<td>
developer.defeatI18nCache = true
</td>
<td>
false
</td>
</tr>
<tr>
<td colspan="2">
<b>For developers only.</b>
Add starting and ending delimiters to each Freemarker template, so you can see
which template were invoked by viewing the generated HTML.
The default is <code>false</code>.
<br/><b>Setting this option to "true" slows down Vitro performance.</b>
</td>
</tr>
<tr class="odd_row blue">
<td>
developer.insertFreemarkerDelimiters = true
</td>
<td>
false
</td>
</tr>
</tbody>
</table>

View file

@ -263,7 +263,12 @@
<target name="prepareVitroHomeDir" depends="prepare">
<mkdir dir="${vitrohome.build.dir}" />
<mkdir dir="${vitrohome.image.dir}" />
<copy todir="${vitrohome.image.dir}" file="${appbase.dir}/config/example.runtime.properties" />
<copy todir="${vitrohome.image.dir}" >
<fileset dir="${appbase.dir}/config" >
<include name="example.runtime.properties" />
<include name="example.developer.properties" />
</fileset>
</copy>
<copy todir="${vitrohome.image.dir}" >
<fileset dir="${appbase.dir}" >
<include name="rdf/**/*" />

View file

@ -0,0 +1,135 @@
#
# -----------------------------------------------------------------------------
# Runtime properties for developer mode.
#
# If the developer.properties file is present in your VIVO home directory, it
# will be loaded as VIVO starts up, taking effect immediately.
#
# Each of these properties can be set or changed while VIVO is running, but it
# can be convenient to set them in advance.
#
# WARNING: Some of these options can seriously degrade performance. They should
# not be enabled in a production instance of VIVO.
#
# -----------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
# General options
#------------------------------------------------------------------------------
#
# The "master switch" for developer mode. If this is not set to true, then none
# of the other properties have any effect.
#
# developer.enabled = true
#
# If developer mode is enabled, this will determine who can modify the
# developer settings. If 'true', then any user can modify the settings. If
# false, then only a site administrator (or root) can modify the settings.
# The default is 'false'.
#
# developer.permitAnonymousControl
#------------------------------------------------------------------------------
# Freemarker
#------------------------------------------------------------------------------
#
# Add HTML comments to each Freemarker template, so you can see what each
# templates to the page, by viewing the source of the page in the browser.
# The default is 'false'.
#
# developer.insertFreemarkerDelimiters = true
#
# Defeat the Freemarker template cache, so each template is read from disk
# on each request. This permits developers to immediately see the effect of
# changes to the template. The default is 'false', which means that a cached
# copy of each template will be used for 60 seconds before the disk is checked
# for a new version.
#
# developer.defeatFreemarkerCache = true
#------------------------------------------------------------------------------
# Page configuration
#------------------------------------------------------------------------------
#
# Turn on logging of custom list view configuration files. Each time a property
# uses a list view other than the default, note it in the log. The default is
# 'false'.
#
# developer.pageContents.logCustomListView = true
#
# Turn on logging of custom short views. Each time an individual uses a short
# view other than the default, note it in the log. The default is 'false'.
#
# developer.pageContents.logCustomShortView = true
#------------------------------------------------------------------------------
# Internationalization
#------------------------------------------------------------------------------
#
# Defeat the cache of language-specific text strings, so the language file
# is read from disk on each request. This permits developers to immediately
# see the effect of changes to the text strings. The default is 'false', which
# means that the language file is only read when VIVO starts up, or when a new
# theme is selected.
#
# developer.i18n.defeatCache = true
#
# Write a line to the log every time a template or a controller requests a
# language-specific string from the properties files.
#
# developer.i18n.logStringRequests
#------------------------------------------------------------------------------
# Logging SPARQL queries
#------------------------------------------------------------------------------
#
# Turn on logging of all SPARQL queries. The logging is at the INFO level.
# Each entry includes:
# - the elapsed time spent on the query, in seconds,
# - the name of the method on RDFService that received the query,
# - the format of the result stream from the RDFService method,
# - the text of the query.
# Note that all access to the content models is done through SPARQL queries,
# but some go through translation layers before reaching the RDFService for
# logging and execution. The default is 'false'.
#
# developer.loggingRDFService.enable = true
#
# If SPARQL query logging is enabled, this will add a stack trace to each log
# entry. The stack trace is abridged, so it starts after the
# ApplicationFilterChain, omits any Jena classes, and ends at the RDFService.
# The default is 'false'.
#
# developer.loggingRDFService.stackTrace = true
#
# If SPARQL query logging is enabled, restrict the number of log entries by
# matching a regular expression against the query string. If the expression
# doesn't match the string, then no log entry is made. The default is "",
# which means no restriction.
#
# developer.loggingRDFService.queryRestriction = .*
#
# If SPARQL query logging is enabled, restrict the number of log entries by
# matching a regular expression against the stack trace. The abridged stack
# trace is concatenated into a single string of fully qualified class names
# and method names. If the expression doesn't match the string, then no log
# entry is made. The default is "", which means no restriction.
#
# developer.loggingRDFService.stackRestriction = .*

View file

@ -116,9 +116,9 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing
#
# Show only the most appropriate data values based on the Accept-Language
# header supplied by the browser. Default is true if not set.
# header supplied by the browser. Default is false if not set.
#
RDFService.languageFilter = true
# RDFService.languageFilter = true
#
# Tell VIVO to generate HTTP headers on its responses to facilitate caching the
@ -152,35 +152,3 @@ RDFService.languageFilter = true
# This should not be used with languages.forceLocale, which will override it.
#
# languages.selectableLocales = en, es, fr
#
# For developers only: Setting this option to "true" slows down Vitro performance.
#
# Defeat the Freemarker template cache, so each template is read from disk
# on each request. This permits developers to immediately see the effect of
# changes to the template. The default is <code>false</code>, which means
# that a cached copy of each template will be used for 60 seconds before
# the disk is checked for a new version.
#
# developer.defeatFreemarkerCache = true
#
# For developers only: Setting this option to "true" slows down Vitro performance.
#
# Defeat the cache of language-specific text strings, so the language file
# is read from disk on each request. This permits developers to immediately
# see the effect of changes to the text strings. The default is
# <code>false</code>, which means that the language file is read when
# VIVO starts up, or when a new theme is selected.
#
# developer.defeatI18nCache = true
#
# For developers only: Setting this option to "true" slows down Vitro performance.
#
# Add starting and ending delimiters to each Freemarker template, so you can see
# which template were invoked by viewing the generated HTML. The default is
# <code>false</code>.
#
# developer.insertFreemarkerDelimiters = true

View file

@ -444,7 +444,7 @@ please_create = Por favor, cree
a_classgroup = un grupo de clase
associate_classes_with_group = y las clases asociadas con el grupo creado.
refresh_content = Actualizar contenido
site_maintenance = Mantenimiento del sitio
rebuild_search_index = Reconstruir índice de búsqueda
rebuild_vis_cache = Reconstruir caché de visualización
recompute_inferences_mixed_caps = Inferencias Recompute
@ -481,6 +481,9 @@ restrict_logins_mixed_caps = Restringir conexiones
site_information = Información del sitio
user_accounts = Las cuentas de usuario
activate_developer_panel = Activar el panel desarrollador
activate_developer_panel_mixed_caps = Activar el panel desarrollador
#
# search controller ( PagedSearchController.java )
#

View file

@ -24,6 +24,7 @@ auth:ADMIN
auth:hasPermission simplePermission:UseMiscellaneousAdminPages ;
auth:hasPermission simplePermission:UseSparqlQueryPage ;
auth:hasPermission simplePermission:PageViewableAdmin ;
auth:hasPermission simplePermission:EnableDeveloperPanel ;
# permissions for CURATOR and above.
auth:hasPermission simplePermission:EditOntology ;

View file

@ -76,6 +76,8 @@ public class SimplePermission extends Permission {
NAMESPACE + "UseAdvancedDataToolsPages");
public static final SimplePermission USE_SPARQL_QUERY_PAGE = new SimplePermission(
NAMESPACE + "UseSparqlQueryPage");
public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission(
NAMESPACE + "EnableDeveloperPanel");
// ----------------------------------------------------------------------

View file

@ -57,7 +57,7 @@ public class Classes2ClassesOperationController extends BaseEditController {
return;
}
VClassDao vcDao = request.getUnfilteredAssertionsWebappDaoFactory().getVClassDao();
VClassDao vcDao = request.getLanguageNeutralWebappDaoFactory().getVClassDao();
String modeStr = request.getParameter("opMode");
modeStr = (modeStr == null) ? "" : modeStr;

View file

@ -53,14 +53,14 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
body.put("dataInput", getDataInputData(vreq));
body.put("siteConfig", getSiteConfigData(vreq));
body.put("indexCacheRebuild", getIndexCacheRebuildUrls(vreq));
body.put("siteMaintenance", getSiteMaintenanceUrls(vreq));
body.put("ontologyEditor", getOntologyEditorData(vreq));
body.put("dataTools", getDataToolsUrls(vreq));
return new TemplateResponseValues(TEMPLATE_DEFAULT, body);
}
protected Map<String, String> getIndexCacheRebuildUrls(VitroRequest vreq) {
protected Map<String, String> getSiteMaintenanceUrls(VitroRequest vreq) {
Map<String, String> urls = new HashMap<String, String>();
@ -73,6 +73,14 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
urls.put("rebuildSearchIndex", UrlBuilder.getUrl("/SearchIndex"));
}
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.LOGIN_DURING_MAINTENANCE.ACTIONS)) {
urls.put("restrictLogins", UrlBuilder.getUrl("/admin/restrictLogins"));
}
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.ENABLE_DEVELOPER_PANEL.ACTIONS)) {
urls.put("activateDeveloperPanel", "javascript:new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({developerEnabled: true});");
}
return urls;
}
@ -143,10 +151,6 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
data.put("startupStatusAlert", !StartupStatus.getBean(getServletContext()).allClear());
}
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.LOGIN_DURING_MAINTENANCE.ACTIONS)) {
data.put("restrictLogins", UrlBuilder.getUrl("/admin/restrictLogins"));
}
return data;
}

View file

@ -167,21 +167,21 @@ public class DeletePropertyController extends FreemarkerHttpServlet {
}
//process object property
private void processObjectProperty(VitroRequest vreq) {
ObjectProperty prop = EditConfigurationUtils.getObjectProperty(vreq);
//if this property is true, it means the object needs to be deleted along with statement
if(prop.getStubObjectRelation())
//while the second test is to see if a different object uri (i.e. not the direct objet of the predicate)
//needs to be deleted
if(prop.getStubObjectRelation() || hasDeleteObjectUri(vreq))
{
deleteObjectIndividual(vreq);
}
if(!hasDeleteObjectUri(vreq)) {
deleteObjectPropertyStatement(vreq);
}
}
@ -194,7 +194,7 @@ public class DeletePropertyController extends FreemarkerHttpServlet {
wdf.getPropertyInstanceDao().deleteObjectPropertyStatement(subjectUri, predicateUri, objectUri);
}
private Individual getObjectIndividualForStubRelation(VitroRequest vreq, String objectUri) {
private Individual getObjectIndividualForDeletion(VitroRequest vreq, String objectUri) {
Individual object = EditConfigurationUtils.getIndividual(vreq, objectUri);
if(object == null) {
@ -208,9 +208,13 @@ public class DeletePropertyController extends FreemarkerHttpServlet {
private void deleteObjectIndividual(VitroRequest vreq) {
String objectUri = EditConfigurationUtils.getObjectUri(vreq);
Individual object = getObjectIndividualForStubRelation(vreq, objectUri);
if(hasDeleteObjectUri(vreq)) {
//if a different individual needs to be deleted, get that uri instead
objectUri = getDeleteObjectUri(vreq);
}
Individual object = getObjectIndividualForDeletion(vreq, objectUri);
if(object != null) {
log.warn("Deleting individual " + object.getName() + "since property has been set to force range object deletion");
log.warn("Deleting individual " + object.getName() + "since property has been set to force range object deletion or has been set to delete a specific object");
WebappDaoFactory wdf = vreq.getWebappDaoFactory();
wdf.getIndividualDao().deleteIndividual(object);
} else {
@ -219,6 +223,16 @@ public class DeletePropertyController extends FreemarkerHttpServlet {
}
}
//This checks if the object uri is not the individual to be deleted but another individual connected
private String getDeleteObjectUri(VitroRequest vreq) {
return (String) vreq.getParameter("deleteObjectUri");
}
private boolean hasDeleteObjectUri(VitroRequest vreq) {
String deleteObjectUri = getDeleteObjectUri(vreq);
return (deleteObjectUri != null && !deleteObjectUri.isEmpty());
}

View file

@ -38,6 +38,7 @@ public class DelimitingTemplateLoader implements TemplateLoader {
@Override
public Object findTemplateSource(String name) throws IOException {
Object innerTS = innerLoader.findTemplateSource(name);
log.debug("template source for '" + name + "' is '" + innerTS + "'");
if (innerTS == null) {
return null;
} else {

View file

@ -1,162 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import freemarker.cache.TemplateLoader;
/**
* <p>
* A {@link TemplateLoader} that treats a directory and its sub-directories as a
* flat namespace.
* </p>
* <p>
* When a request is made to find a template source, the loader will search its
* base directory and any sub-directories for a file with a matching name. So a
* request for <code>myFile.ftl</code> might return a reference to a file at
* <code>base/myFile.ftl</code> or at <code>base/this/myFile.ftl</code>
* </p>
* <p>
* The order in which the sub-directories are searched is unspecified. The first
* matching file will be returned.
* </p>
* <p>
* A path (absolute or relative) on the source name would be meaningless, so any
* such path will be stripped before the search is made. That is, a request for
* <code>path/file.ftl</code> or <code>/absolute/path/file.ftl</code>is
* functionally identical to a request for <code>file.ftl</code>
* </p>
* <p>
* </p>
*/
public class FlatteningTemplateLoader implements TemplateLoader {
private static final Log log = LogFactory
.getLog(FlatteningTemplateLoader.class);
private final File baseDir;
public FlatteningTemplateLoader(File baseDir) {
if (baseDir == null) {
throw new NullPointerException("baseDir may not be null.");
}
if (!baseDir.exists()) {
throw new IllegalArgumentException("Template directory '"
+ baseDir.getAbsolutePath() + "' does not exist");
}
if (!baseDir.isDirectory()) {
throw new IllegalArgumentException("Template directory '"
+ baseDir.getAbsolutePath() + "' is not a directory");
}
if (!baseDir.canRead()) {
throw new IllegalArgumentException("Can't read template "
+ "directory '" + baseDir.getAbsolutePath() + "'");
}
log.debug("Created template loader - baseDir is '"
+ baseDir.getAbsolutePath() + "'");
this.baseDir = baseDir;
}
/**
* Look for a file by this name in the base directory, or its
* subdirectories, disregarding any path information.
*
* @return a {@link File} that can be used in subsequent calls the template
* loader methods, or <code>null</code> if no template is found.
*/
@Override
public Object findTemplateSource(String name) throws IOException {
if (name == null) {
return null;
}
int lastSlashHere = name.indexOf('/');
String trimmedName = (lastSlashHere == -1) ? name : name
.substring(lastSlashHere + 1);
// start the recursive search.
File source = findFile(trimmedName, baseDir);
if (source == null) {
log.debug("For template name '" + name
+ "', found no template file.");
} else {
log.debug("For template name '" + name + "', template file is "
+ source.getAbsolutePath());
}
return source;
}
/**
* Recursively search for a file of this name.
*/
private File findFile(String name, File dir) {
for (File child : dir.listFiles()) {
if (child.isDirectory()) {
File file = findFile(name, child);
if (file != null) {
return file;
}
} else {
if (child.getName().equals(name)) {
return child;
}
}
}
return null;
}
/**
* Ask the file when it was last modified.
*
* @param templateSource
* a {@link File} that was obtained earlier from
* {@link #findTemplateSource(String)}.
*/
@Override
public long getLastModified(Object templateSource) {
if (!(templateSource instanceof File)) {
throw new IllegalArgumentException("templateSource is not a File: "
+ templateSource);
}
return ((File) templateSource).lastModified();
}
/**
* Get a {@link Reader} on this {@link File}. The framework will see that
* the {@link Reader} is closed when it has been read.
*
* @param templateSource
* a {@link File} that was obtained earlier from
* {@link #findTemplateSource(String)}.
*/
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
if (!(templateSource instanceof File)) {
throw new IllegalArgumentException("templateSource is not a File: "
+ templateSource);
}
return new FileReader(((File) templateSource));
}
/**
* Nothing to do here. No resources to free up.
*
* @param templateSource
* a {@link File} that was obtained earlier from
* {@link #findTemplateSource(String)}.
*/
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
}
}

View file

@ -56,7 +56,7 @@ public class SimpleReasonerRecomputeController extends FreemarkerHttpServlet {
"SimpleReasonerRecomputController.Recomputer");
thread.setWorkLevel(WORKING);
thread.start();
messageStr = "Recompute of inferences started. See vivo log for further details.";
messageStr = "Recompute of inferences started. See log for further details.";
} else {
body.put("formAction", UrlBuilder.getUrl("/RecomputeInferences"));
}

View file

@ -71,8 +71,7 @@ public class ObjectPropertyDaoJena extends PropertyDaoJena implements ObjectProp
}
public void deleteObjectProperty(String propertyURI) {
ObjectProperty op = new ObjectProperty();
op.setURI(propertyURI);
ObjectProperty op = getObjectPropertyByURI(propertyURI);
deleteObjectProperty(op);
}

View file

@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -116,6 +117,10 @@ public class QueryUtils {
return getQueryResults(queryStr, vreq.getRDFService());
}
public static ResultSet getQueryResults(String queryStr, QuerySolution initialBindings, RDFService rdfService) {
return getQueryResults(bindVariables(queryStr, initialBindings), rdfService);
}
public static ResultSet getLanguageNeutralQueryResults(String queryStr, VitroRequest vreq) {
return getQueryResults(queryStr, vreq.getUnfilteredRDFService());
}
@ -130,4 +135,38 @@ public class QueryUtils {
}
}
/**
* The RDFService interface doesn't support initial bindings, so do text
* substitutions instead.
*/
public static String bindVariables(String queryStr,
QuerySolution initialBindings) {
String bound = queryStr;
for (Iterator<String> it = initialBindings.varNames(); it.hasNext();) {
String name = it.next();
RDFNode node = initialBindings.get(name);
if (node.isLiteral()) {
bound = bound.replace('?' + name, literalToString(node.asLiteral()));
} else if (node.isURIResource()) {
bound = bound.replace('?' + name, '<'+node.asResource().getURI()+ '>');
}else {
log.warn("Failed to bind anonymous resource variable '" + name
+ "' to query '" + bound + "'");
}
}
return bound;
}
private static String literalToString(Literal l) {
StringBuilder buffer = new StringBuilder();
buffer.append('"').append(l.getLexicalForm()).append('"');
if (l.getDatatypeURI() != null) {
buffer.append("^^<").append(l.getDatatypeURI()).append(">");
} else if (StringUtils.isNotEmpty(l.getLanguage())) {
buffer.append("@").append(l.getLanguage());
}
return buffer.toString();
}
}

View file

@ -1048,11 +1048,20 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao {
try {
OntResource subclass = getOntClass(ontModel,c2c.getSubclassURI());
OntResource superclass = getOntClass(ontModel,c2c.getSuperclassURI());
if(subclass == null || superclass == null) {
log.warn("unable to delete " + c2c.getSubclassURI() +
" rdfs:subClassOf " + c2c.getSuperclassURI());
if (subclass == null) {
log.warn(c2c.getSubclassURI() + " not found in the model.");
}
if (superclass == null) {
log.warn(c2c.getSuperclassURI() + " not found in the model.");
}
return;
}
Model removal = ModelFactory.createDefaultModel();
Model additions = ModelFactory.createDefaultModel(); // to repair any rdf:Lists
if ((subclass != null) && (superclass != null)) {
removal.add(ontModel.listStatements(subclass, RDFS.subClassOf, superclass));
}
if (subclass.isAnon()) {
Model[] changeSet = getSmartRemoval(subclass, getOntModel());
removal.add(changeSet[0]);

View file

@ -56,7 +56,10 @@ public class DefaultAddMissingIndividualFormGenerator implements EditConfigurati
public static boolean isCreateNewIndividual(VitroRequest vreq, HttpSession session) {
String command = vreq.getParameter("cmd");
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
ObjectProperty objProp = vreq.getWebappDaoFactory().getObjectPropertyDao().getObjectPropertyByURI(predicateUri);
//This method also looks at domain and range uris and so is different than just getting the
//object property based on predicate uri alone
ObjectProperty objProp = EditConfigurationUtils.getObjectPropertyForPredicate(vreq,
predicateUri);
if(objProp != null) {
return(objProp.getOfferCreateNewOption() &&
(

View file

@ -336,11 +336,11 @@ public class RequestModelsPrep implements Filter {
}
/**
* Language awareness is enabled unless they explicitly disable it.
* Language awareness is disabled unless they explicitly enable it.
*/
private Boolean isLanguageAwarenessEnabled() {
return Boolean.valueOf(props.getProperty("RDFService.languageFilter",
"true"));
"false"));
}
private RDFService addLanguageAwareness(HttpServletRequest req,

View file

@ -3,7 +3,6 @@
package edu.cornell.mannlib.vitro.webapp.freemarker.config;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -18,15 +17,16 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.DelimitingTemplateLoader;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FlatteningTemplateLoader;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants;
import edu.cornell.mannlib.vitro.webapp.freemarker.loader.FreemarkerTemplateLoader;
import edu.cornell.mannlib.vitro.webapp.i18n.freemarker.I18nMethodModel;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
import edu.cornell.mannlib.vitro.webapp.web.directives.IndividualShortViewDirective;
import edu.cornell.mannlib.vitro.webapp.web.directives.UrlDirective;
import edu.cornell.mannlib.vitro.webapp.web.directives.WidgetDirective;
@ -34,7 +34,6 @@ 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.IndividualProfileUrlMethod;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.ext.beans.BeansWrapper;
@ -55,18 +54,17 @@ import freemarker.template.TemplateModelException;
* own locale, etc.
*
* Each time a request asks for the configuration, check to see whether the
* cache is still valid, and whether the theme has changed (needs a new
* TemplateLoader). Store the request info to the ThreadLocal.
* cache is still valid, whether the theme has changed (needs a new
* TemplateLoader), and whether the DeveloperSettings have changed (might need a
* new TemplateLoader). Store the request info to the ThreadLocal.
*/
public abstract class FreemarkerConfiguration {
private static final Log log = LogFactory
.getLog(FreemarkerConfiguration.class);
private static final String PROPERTY_DEFEAT_CACHE = "developer.defeatFreemarkerCache";
private static final String PROPERTY_INSERT_DELIMITERS = "developer.insertFreemarkerDelimiters";
private static volatile FreemarkerConfigurationImpl instance;
private static volatile String previousThemeDir;
private static volatile Map<String, Object> previousSettingsMap;
public static Configuration getConfig(HttpServletRequest req) {
confirmInstanceIsSet();
@ -92,14 +90,12 @@ public abstract class FreemarkerConfiguration {
}
}
/** If the developer doesn't want the cache, it's invalid. */
private static boolean isTemplateCacheInvalid(HttpServletRequest req) {
ConfigurationProperties props = ConfigurationProperties.getBean(req);
// If the developer doesn't want the cache, it's invalid.
if (Boolean.valueOf(props.getProperty(PROPERTY_DEFEAT_CACHE))) {
DeveloperSettings settings = DeveloperSettings.getBean(req);
if (settings.getBoolean(Keys.DEFEAT_FREEMARKER_CACHE)) {
return true;
}
return false;
}
@ -113,7 +109,8 @@ public abstract class FreemarkerConfiguration {
private static void keepTemplateLoaderCurrentWithThemeDirectory(
HttpServletRequest req) {
String themeDir = getThemeDirectory(req);
if (hasThemeDirectoryChanged(themeDir)) {
if (hasThemeDirectoryChanged(themeDir)
|| haveDeveloperSettingsChanged(req)) {
TemplateLoader tl = createTemplateLoader(req, themeDir);
instance.setTemplateLoader(tl);
}
@ -134,44 +131,48 @@ public abstract class FreemarkerConfiguration {
}
}
private static boolean haveDeveloperSettingsChanged(HttpServletRequest req) {
Map<String, Object> settingsMap = DeveloperSettings.getBean(req)
.getSettingsMap();
if (settingsMap.equals(previousSettingsMap)) {
return false;
} else {
previousSettingsMap = settingsMap;
return true;
}
}
private static TemplateLoader createTemplateLoader(HttpServletRequest req,
String themeDir) {
ServletContext ctx = req.getSession().getServletContext();
ConfigurationProperties props = ConfigurationProperties.getBean(ctx);
List<TemplateLoader> loaders = new ArrayList<TemplateLoader>();
// Theme template loader
// Theme template loader - only if the theme has a template directory.
String themeTemplatePath = ctx.getRealPath(themeDir) + "/templates";
File themeTemplateDir = new File(themeTemplatePath);
// A theme need not contain a template directory.
if (themeTemplateDir.exists()) {
try {
FileTemplateLoader themeFtl = new FileTemplateLoader(
themeTemplateDir);
loaders.add(themeFtl);
} catch (IOException e) {
log.error("Error creating theme template loader", e);
}
loaders.add(new FreemarkerTemplateLoader(themeTemplateDir));
}
// Vitro template loader
String vitroTemplatePath = ctx.getRealPath("/templates/freemarker");
loaders.add(new FlatteningTemplateLoader(new File(vitroTemplatePath)));
loaders.add(new FreemarkerTemplateLoader(new File(vitroTemplatePath)));
// TODO VIVO-243 Why is this here?
loaders.add(new ClassTemplateLoader(FreemarkerConfiguration.class, ""));
TemplateLoader[] loaderArray = loaders
.toArray(new TemplateLoader[loaders.size()]);
MultiTemplateLoader mtl = new MultiTemplateLoader(loaderArray);
TemplateLoader tl = new MultiTemplateLoader(loaderArray);
// If requested, add delimiters to the templates.
if (Boolean.valueOf(props.getProperty(PROPERTY_INSERT_DELIMITERS))) {
return new DelimitingTemplateLoader(mtl);
} else {
return mtl;
DeveloperSettings settings = DeveloperSettings.getBean(req);
if (settings.getBoolean(Keys.INSERT_FREEMARKER_DELIMITERS)) {
tl = new DelimitingTemplateLoader(tl);
}
return tl;
}
private static void setThreadLocalsForRequest(HttpServletRequest req) {

View file

@ -287,6 +287,7 @@ public class FreemarkerConfigurationImpl extends Configuration {
urls.put("images", UrlBuilder.getUrl("/images"));
urls.put("theme", UrlBuilder.getUrl(themeDir));
urls.put("index", UrlBuilder.getUrl("/browse"));
urls.put("developerAjax", UrlBuilder.getUrl("/admin/developerAjax"));
return urls;
}

View file

@ -0,0 +1,323 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.freemarker.loader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import freemarker.cache.TemplateLoader;
/**
* Loads Freemarker templates from a given directory.
*
* Different from a file loader in two ways:
*
* 1) Flattens the directory. When it searches for a template, it will look in
* the base directory and in any sub-directories. While doing this, it ignores
* any path that is attached to the template name.
*
* So if you were to ask for 'admin/silly.ftl', it would search for 'silly.ftl'
* in the base directory, and in any sub-directories, until it finds one.
*
* 2) Accepts approximate matches on locales. When asked for a template, it will
* accepts an approximate match that matches the basename and extension, and
* language or region if specifed. So a search for a template with no language
* or region will prefer an exact match, but will accept one with language or
* both language and region.
*
* <pre>
* "this_es_MX.ftl" matches "this_es_MX.ftl"
* "this_es.ftl" matches "this_es.ftl" or "this_es_MX.ftl"
* "this.ftl" matches "this.ftl" or "this_es.ftl" or "this_es_MX.ftl"
* </pre>
*
* This allows Freemarker to mimic the behavior of the language filtering RDF
* service, because if Freemarker does not find a match for "this_es_MX.ftl", it
* will try again with "this_es.ftl" and "this.ftl". So the net effect is that a
* search for "silly_es_MX.ftl" would eventually return any of these, in order
* of preference:
*
* <pre>
* silly_es_MX.ftl
* silly_es.ftl
* silly_es_*.ftl
* silly.ftl
* silly_*.ftl
* </pre>
*
* If more than one template file qualifies, we choose by best fit, shortest
* path, and alphabetical order, to insure that identical requests produce
* identical results.
*/
public class FreemarkerTemplateLoader implements TemplateLoader {
private static final Log log = LogFactory
.getLog(FreemarkerTemplateLoader.class);
private final File baseDir;
public FreemarkerTemplateLoader(File baseDir) {
if (baseDir == null) {
throw new NullPointerException("baseDir may not be null.");
}
String path = baseDir.getAbsolutePath();
if (!baseDir.exists()) {
throw new IllegalArgumentException("Template directory '" + path
+ "' does not exist");
}
if (!baseDir.isDirectory()) {
throw new IllegalArgumentException("Template directory '" + path
+ "' is not a directory");
}
if (!baseDir.canRead()) {
throw new IllegalArgumentException(
"Can't read template directory '" + path + "'");
}
log.debug("Created template loader - baseDir is '" + path + "'");
this.baseDir = baseDir;
}
/**
* Get the best template for this name. Walk the tree finding all possible
* matches, then choose our favorite.
*/
@Override
public Object findTemplateSource(String name) throws IOException {
if (StringUtils.isBlank(name)) {
return null;
}
SortedSet<PathPieces> matches = findAllMatches(new PathPieces(name));
if (matches.isEmpty()) {
return null;
} else {
return matches.last().path.toFile();
}
}
private SortedSet<PathPieces> findAllMatches(PathPieces searchTerm) {
PathPiecesFileVisitor visitor = new PathPiecesFileVisitor(searchTerm);
try {
Files.walkFileTree(baseDir.toPath(), visitor);
} catch (IOException e) {
log.error(e);
}
return visitor.getMatches();
}
/**
* Ask the file when it was last modified.
*
* @param templateSource
* a File that was obtained earlier from findTemplateSource().
*/
@Override
public long getLastModified(Object templateSource) {
return asFile(templateSource).lastModified();
}
/**
* Get a Reader on this File. The framework will close the Reader after
* reading it.
*
* @param templateSource
* a File that was obtained earlier from findTemplateSource().
*/
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
return new FileReader(asFile(templateSource));
}
/**
* Nothing to do here. No resources to free up.
*
* @param templateSource
* a File that was obtained earlier from findTemplateSource().
*/
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
// Nothing to do.
}
/**
* That templateSource is a File, right?
*/
private File asFile(Object templateSource) {
if (templateSource instanceof File) {
return (File) templateSource;
} else {
throw new IllegalArgumentException("templateSource is not a File: "
+ templateSource);
}
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
/**
* Break a path into handy segments, so we can see whether they match the
* search term, and how well they match.
*/
static class PathPieces {
static final Pattern PATTERN = Pattern.compile("(.+?)" // base name
+ "(_[a-z]{2})?" // optional language
+ "(_[A-Z]{2})?" // optional country
+ "(\\.\\w+)?" // optional extension
);
final Path path;
final String base;
final String language;
final String region;
final String extension;
public PathPieces(String pathString) {
this(Paths.get(pathString));
}
public PathPieces(Path path) {
this.path = path;
String filename = path.getFileName().toString();
Matcher m = PATTERN.matcher(filename);
if (m.matches()) {
base = getGroup(m, 1);
language = getGroup(m, 2);
region = getGroup(m, 3);
extension = getGroup(m, 4);
} else {
base = filename;
language = "";
region = "";
extension = "";
}
}
private String getGroup(Matcher m, int i) {
return (m.start(i) == -1) ? "" : m.group(i);
}
/**
* If I'm searching for this, is that an acceptable match?
*
* Note that this is asymetrical -- a search term without a region will
* match a candidate with a region, but not vice versa. Same with
* language.
*/
public boolean matches(PathPieces that) {
return base.equals(that.base) && extension.equals(that.extension)
&& (language.isEmpty() || language.equals(that.language))
&& (region.isEmpty() || region.equals(that.region));
}
/**
* How good a match is that to this?
*/
public int score(PathPieces that) {
if (matches(that)) {
if (that.language.equals(language)) {
if (that.region.equals(region)) {
return 3; // exact match.
} else {
return 2; // same language, approximate region.
}
} else {
return 1; // approximate language.
}
} else {
return -1; // doesn't match.
}
}
@Override
public String toString() {
return "PathPieces[" + base + ", " + language + ", " + region
+ ", " + extension + "]";
}
}
/**
* While walking the file tree, collect all files that match the search
* term, as a sorted set of PathPieces.
*/
static class PathPiecesFileVisitor extends SimpleFileVisitor<Path> {
private final PathPieces searchTerm;
private final SortedSet<PathPieces> matches;
public PathPiecesFileVisitor(PathPieces searchTerm) {
this.searchTerm = searchTerm;
this.matches = new TreeSet<>(new PathPiecesComparator(searchTerm));
}
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
throws IOException {
if (fileQualifies(path)) {
PathPieces found = new PathPieces(path);
if (searchTerm.matches(found)) {
matches.add(found);
}
}
return FileVisitResult.CONTINUE;
}
public boolean fileQualifies(Path path) {
return Files.isRegularFile(path) && Files.isReadable(path);
}
public SortedSet<PathPieces> getMatches() {
return matches;
}
}
/**
* Produce an ordering of paths by desirability. Best match, then shortest
* directory path, and finally alphabetical order.
*/
static class PathPiecesComparator implements Comparator<PathPieces> {
private final PathPieces searchFor;
public PathPiecesComparator(PathPieces searchFor) {
this.searchFor = searchFor;
}
@Override
public int compare(PathPieces p1, PathPieces p2) {
int scoring = searchFor.score(p1) - searchFor.score(p2);
if (scoring != 0) {
return scoring; // prefer matches to region and language
}
int pathLength = p1.path.getNameCount() - p2.path.getNameCount();
if (pathLength != 0) {
return -pathLength; // shorter is better
}
return -p1.path.compareTo(p2.path); // early in alphabet is better
}
}
}

View file

@ -3,10 +3,14 @@
package edu.cornell.mannlib.vitro.webapp.i18n;
import java.io.IOException;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletContext;
@ -15,8 +19,10 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
/**
* Provides access to a bundle of text strings, based on the name of the bundle,
@ -31,7 +37,6 @@ public class I18n {
private static final Log log = LogFactory.getLog(I18n.class);
public static final String DEFAULT_BUNDLE_NAME = "all";
private static final String PROPERTY_DEVELOPER_DEFEAT_CACHE = "developer.defeatI18nCache";
/**
* If this attribute is present on the request, then the cache has already
@ -103,6 +108,7 @@ public class I18n {
protected I18nBundle getBundle(String bundleName, HttpServletRequest req) {
log.debug("Getting bundle '" + bundleName + "'");
I18nLogger i18nLogger = new I18nLogger(req);
try {
checkDevelopmentMode(req);
checkForChangeInThemeDirectory(req);
@ -113,13 +119,13 @@ public class I18n {
ResourceBundle.Control control = new ThemeBasedControl(ctx, dir);
ResourceBundle rb = ResourceBundle.getBundle(bundleName,
req.getLocale(), control);
return new I18nBundle(bundleName, rb);
return new I18nBundle(bundleName, rb, i18nLogger);
} catch (MissingResourceException e) {
log.warn("Didn't find text bundle '" + bundleName + "'");
return I18nBundle.emptyBundle(bundleName);
return I18nBundle.emptyBundle(bundleName, i18nLogger);
} catch (Exception e) {
log.error("Failed to create text bundle '" + bundleName + "'", e);
return I18nBundle.emptyBundle(bundleName);
return I18nBundle.emptyBundle(bundleName, i18nLogger);
}
}
@ -127,11 +133,7 @@ public class I18n {
* If we are in development mode, clear the cache on each request.
*/
private void checkDevelopmentMode(HttpServletRequest req) {
ConfigurationProperties bean = ConfigurationProperties.getBean(req);
String flag = bean
.getProperty(PROPERTY_DEVELOPER_DEFEAT_CACHE, "false");
if (Boolean.valueOf(flag.trim())) {
if (DeveloperSettings.getBean(req).getBoolean(Keys.I18N_DEFEAT_CACHE)) {
log.debug("In development mode - clearing the cache.");
clearCacheOnRequest(req);
}
@ -170,7 +172,7 @@ public class I18n {
* Instead of looking in the classpath, look in the theme i18n directory and
* the application i18n directory.
*/
private static class ThemeBasedControl extends ResourceBundle.Control {
static class ThemeBasedControl extends ResourceBundle.Control {
private static final String BUNDLE_DIRECTORY = "i18n/";
private final ServletContext ctx;
private final String themeDirectory;
@ -218,6 +220,52 @@ public class I18n {
themeI18nPath, this);
}
/**
* When creating the chain of acceptable Locales, include approximate
* matches before giving up and using the root Locale.
*
* Check the list of supported Locales to see if any have the same
* language but different region. If we find any, sort them and insert
* them into the usual result list, just before the root Locale.
*/
@Override
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
// Find the list of Locales that would normally be returned.
List<Locale> usualList = super
.getCandidateLocales(baseName, locale);
// If our "selectable locales" include no approximate matches that
// are not already in the list, we're done.
SortedSet<Locale> approximateMatches = findApproximateMatches(locale);
approximateMatches.removeAll(usualList);
if (approximateMatches.isEmpty()) {
return usualList;
}
// Otherwise, insert those approximate matches into the list just
// before the ROOT locale.
List<Locale> mergedList = new LinkedList<>(usualList);
int rootLocaleHere = mergedList.indexOf(Locale.ROOT);
if (rootLocaleHere == -1) {
mergedList.addAll(approximateMatches);
} else {
mergedList.addAll(rootLocaleHere, approximateMatches);
}
return mergedList;
}
private SortedSet<Locale> findApproximateMatches(Locale locale) {
SortedSet<Locale> set = new TreeSet<>(new LocaleComparator());
for (Locale l : SelectedLocale.getSelectableLocales(ctx)) {
if (locale.getLanguage().equals(l.getLanguage())) {
set.add(l);
}
}
return set;
}
/**
* The documentation for ResourceBundle.Control.newBundle() says I
* should throw these exceptions.
@ -238,5 +286,20 @@ public class I18n {
"format must be one of these: " + FORMAT_DEFAULT);
}
}
}
private static class LocaleComparator implements Comparator<Locale> {
@Override
public int compare(Locale o1, Locale o2) {
int c = o1.getLanguage().compareTo(o2.getLanguage());
if (c == 0) {
c = o1.getCountry().compareTo(o2.getCountry());
if (c == 0) {
c = o1.getVariant().compareTo(o2.getVariant());
}
}
return c;
}
}
}

View file

@ -24,24 +24,28 @@ public class I18nBundle {
private static final String MESSAGE_BUNDLE_NOT_FOUND = "Text bundle ''{0}'' not found.";
private static final String MESSAGE_KEY_NOT_FOUND = "Text bundle ''{0}'' has no text for ''{1}''";
public static I18nBundle emptyBundle(String bundleName) {
return new I18nBundle(bundleName);
public static I18nBundle emptyBundle(String bundleName,
I18nLogger i18nLogger) {
return new I18nBundle(bundleName, i18nLogger);
}
private final String bundleName;
private final ResourceBundle resources;
private final String notFoundMessage;
private final I18nLogger i18nLogger;
private I18nBundle(String bundleName) {
this(bundleName, new EmptyResourceBundle(), MESSAGE_BUNDLE_NOT_FOUND);
private I18nBundle(String bundleName, I18nLogger i18nLogger) {
this(bundleName, new EmptyResourceBundle(), MESSAGE_BUNDLE_NOT_FOUND,
i18nLogger);
}
public I18nBundle(String bundleName, ResourceBundle resources) {
this(bundleName, resources, MESSAGE_KEY_NOT_FOUND);
public I18nBundle(String bundleName, ResourceBundle resources,
I18nLogger i18nLogger) {
this(bundleName, resources, MESSAGE_KEY_NOT_FOUND, i18nLogger);
}
private I18nBundle(String bundleName, ResourceBundle resources,
String notFoundMessage) {
String notFoundMessage, I18nLogger i18nLogger) {
if (bundleName == null) {
throw new IllegalArgumentException("bundleName may not be null");
}
@ -57,22 +61,27 @@ public class I18nBundle {
this.bundleName = bundleName;
this.resources = resources;
this.notFoundMessage = notFoundMessage;
this.i18nLogger = i18nLogger;
}
public String text(String key, Object... parameters) {
String textString;
if (resources.containsKey(key)) {
textString = resources.getString(key);
log.debug("In '" + bundleName + "', " + key + "='" + textString
+ "')");
return formatString(textString, parameters);
} else {
String message = MessageFormat.format(notFoundMessage, bundleName,
key);
log.warn(message);
return "ERROR: " + message;
textString = "ERROR: " + message;
}
String result = formatString(textString, parameters);
if (i18nLogger != null) {
i18nLogger.log(bundleName, key, parameters, textString, result);
}
return result;
}
private static String formatString(String textString, Object... parameters) {

View file

@ -0,0 +1,47 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.i18n;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
/**
* If enabled in developer mode, write a message to the log each time someone
* asks for a language string.
*
* The I18nBundle has a life span of one HTTP request, and so does this.
*/
public class I18nLogger {
private static final Log log = LogFactory.getLog(I18nLogger.class);
private final boolean isLogging;
public I18nLogger(HttpServletRequest req) {
DeveloperSettings settings = DeveloperSettings.getBean(req);
this.isLogging = settings.getBoolean(Keys.ENABLED)
&& settings.getBoolean(Keys.I18N_LOG_STRINGS)
&& log.isInfoEnabled();
}
public void log(String bundleName, String key, Object[] parameters,
String rawText, String formattedText) {
if (isLogging) {
String message = String.format(
"Retrieved from %s.%s with %s: '%s'", bundleName, key,
Arrays.toString(parameters), rawText);
if (!rawText.equals(formattedText)) {
message += String.format(" --> '%s'", formattedText);
}
log.info(message);
}
}
}

View file

@ -134,7 +134,8 @@ public class LocaleSelectionSetup implements ServletContextListener {
throws IllegalArgumentException {
Locale locale = LocaleUtils.toLocale(localeString);
if (!LocaleUtils.isAvailableLocale(locale)) {
if (!"es_GO".equals(localeString) && // No complaint about bogus locale
!LocaleUtils.isAvailableLocale(locale)) {
ssWarning("'" + locale + "' is not a recognized locale.");
}
return locale;

View file

@ -2,6 +2,7 @@
package edu.cornell.mannlib.vitro.webapp.i18n.selection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@ -124,7 +125,14 @@ public abstract class SelectedLocale {
* an empty list, but never returns null.
*/
public static List<Locale> getSelectableLocales(HttpServletRequest req) {
ServletContext ctx = req.getSession().getServletContext();
return getSelectableLocales(req.getSession().getServletContext());
}
/**
* Get the list of selectable Locales from the servlet context. May return
* an empty list, but never returns null.
*/
public static List<Locale> getSelectableLocales(ServletContext ctx) {
Object ctxInfo = ctx.getAttribute(ATTRIBUTE_NAME);
if (ctxInfo instanceof ContextSelectedLocale) {
List<Locale> selectableLocales = ((ContextSelectedLocale) ctxInfo)
@ -164,7 +172,8 @@ public abstract class SelectedLocale {
}
this.forcedLocale = null;
this.selectableLocales = selectableLocales;
this.selectableLocales = Collections
.unmodifiableList(new ArrayList<>(selectableLocales));
}
public Locale getForcedLocale() {

View file

@ -207,6 +207,14 @@ public class KnowledgeBaseUpdater {
StmtIterator sit = anonModel.listStatements();
while (sit.hasNext()) {
Statement stmt = sit.nextStatement();
// Skip statements with blank nodes (unsupported) to avoid
// excessive deletion. In the future, the whole updater
// could be modified to change whole graphs at once through
// the RDFService, but right now this whole thing is statement
// based.
if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) {
continue;
}
Iterator<String> graphIt = dataset.listNames();
while(graphIt.hasNext()) {
String graph = graphIt.next();
@ -223,8 +231,9 @@ public class KnowledgeBaseUpdater {
//log.info("removed " + anonModel.size() + " statements from SPARQL CONSTRUCTs");
} else {
Model writeModel = dataset.getNamedModel(JenaDataSourceSetupBase.JENA_DB_MODEL);
Model dedupeModel = dataset.getDefaultModel();
Model additions = jiu.renameBNodes(
anonModel, settings.getDefaultNamespace() + "n", writeModel);
anonModel, settings.getDefaultNamespace() + "n", dedupeModel);
Model actualAdditions = ModelFactory.createDefaultModel();
StmtIterator stmtIt = additions.listStatements();
while (stmtIt.hasNext()) {
@ -346,7 +355,9 @@ public class KnowledgeBaseUpdater {
}
public static boolean isUpdatableABoxGraph(String graphName) {
return (!graphName.contains("tbox") && !graphName.contains("filegraph"));
return (graphName != null && !graphName.contains("tbox")
&& !graphName.contains("filegraph")
&& !graphName.contains("x-arq:UnionGraph"));
}
/**

View file

@ -217,6 +217,9 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
private List<Statement> sort(List<Statement> stmts) {
List<Statement> output = new ArrayList<Statement>();
int originalSize = stmts.size();
if(originalSize == 1) {
return stmts;
}
List <Statement> remaining = stmts;
ConcurrentLinkedQueue<Resource> subjQueue = new ConcurrentLinkedQueue<Resource>();
for(Statement stmt : remaining) {

View file

@ -15,16 +15,20 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
/**
* Writes the log message for the LoggingRDFService.
*
* If not enabled, or if the logging level is insufficient, this does nothing.
*
* If enabled, it checks for restrictions. If there is a restriction pattern
* (regular expression), the a log message will only be printed if one of the
* fully-qualified class names in the stack trace matches that pattern.
* If enabled, it checks for restrictions. If there is a restriction on the call
* stack (regular expression), then a log message will only be printed if the
* pattern is found in the concatenated call stack (fully-qualified class names
* and method names). If there is a restriction on the query string (regular
* expression) then a log message will only be printed if the pattern is found
* in the query string.
*
* If everything passes muster, the constructor will record the time that the
* instance was created.
@ -41,16 +45,13 @@ import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
public class RDFServiceLogger implements AutoCloseable {
private static final Log log = LogFactory.getLog(RDFServiceLogger.class);
private static final String PROPERTY_ENABLED = "developer.loggingRDFService.enable";
private static final String PROPERTY_STACK_TRACE = "developer.loggingRDFService.stackTrace";
private static final String PROPERTY_RESTRICTION = "developer.loggingRDFService.restriction";
private final ServletContext ctx;
private final Object[] args;
private boolean isEnabled;
private boolean traceRequested;
private Pattern restriction;
private Pattern queryStringRestriction;
private Pattern callStackRestriction;
private String methodName;
private List<StackTraceElement> trace = Collections.emptyList();
@ -65,27 +66,33 @@ public class RDFServiceLogger implements AutoCloseable {
if (isEnabled && log.isInfoEnabled()) {
loadStackTrace();
if (passesRestrictions()) {
if (passesQueryRestriction() && passesStackRestriction()) {
this.startTime = System.currentTimeMillis();
}
}
}
private void getProperties() {
ConfigurationProperties props = ConfigurationProperties.getBean(ctx);
isEnabled = Boolean.valueOf(props.getProperty(PROPERTY_ENABLED));
traceRequested = Boolean.valueOf(props
.getProperty(PROPERTY_STACK_TRACE));
String restrictionString = props.getProperty(PROPERTY_RESTRICTION);
if (StringUtils.isNotBlank(restrictionString)) {
try {
restriction = Pattern.compile(restrictionString);
} catch (Exception e) {
log.error("Failed to compile the pattern for "
+ PROPERTY_RESTRICTION + " = " + restriction + " " + e);
isEnabled = false;
DeveloperSettings settings = DeveloperSettings.getBean(ctx);
isEnabled = settings.getBoolean(Keys.LOGGING_RDF_ENABLE);
traceRequested = settings.getBoolean(Keys.LOGGING_RDF_STACK_TRACE);
queryStringRestriction = patternFromSettings(settings,
Keys.LOGGING_RDF_QUERY_RESTRICTION);
callStackRestriction = patternFromSettings(settings,
Keys.LOGGING_RDF_STACK_RESTRICTION);
}
private Pattern patternFromSettings(DeveloperSettings settings, Keys key) {
String patternString = settings.getString(key);
if (StringUtils.isBlank(patternString)) {
return null;
}
try {
return Pattern.compile(patternString);
} catch (Exception e) {
log.error("Failed to compile the pattern for " + key + " = "
+ patternString + " " + e);
return Pattern.compile("^_____NEVER MATCH_____$");
}
}
@ -144,16 +151,39 @@ public class RDFServiceLogger implements AutoCloseable {
}
}
private boolean passesRestrictions() {
if (restriction == null) {
private boolean passesQueryRestriction() {
if (queryStringRestriction == null) {
return true;
}
String q = assembleQueryString();
return queryStringRestriction.matcher(q).find();
}
private String assembleQueryString() {
StringBuilder query = new StringBuilder();
for (Object arg : args) {
if (arg instanceof String) {
query.append((String) arg).append(" ");
}
}
return query.deleteCharAt(query.length() - 1).toString();
}
private boolean passesStackRestriction() {
if (callStackRestriction == null) {
return true;
}
String q = assembleCallStackString();
return callStackRestriction.matcher(q).find();
}
private String assembleCallStackString() {
StringBuilder stack = new StringBuilder();
for (StackTraceElement ste : trace) {
if (restriction.matcher(ste.getClassName()).find()) {
return true;
stack.append(ste.getClassName()).append(" ")
.append(ste.getMethodName()).append(" ");
}
}
return false;
return stack.deleteCharAt(stack.length() - 1).toString();
}
@Override

View file

@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
@ -14,7 +15,9 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -38,6 +41,7 @@ import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.sparql.engine.http.QueryEngineHTTP;
import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
@ -84,11 +88,11 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
this.readRepository = new HTTPRepository(readEndpointURI);
this.updateRepository = new HTTPRepository(updateEndpointURI);
testConnection();
MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
mgr.getParams().setDefaultMaxConnectionsPerHost(10);
mgr.getParams().setDefaultMaxConnectionsPerHost(50);
this.httpClient = new HttpClient(mgr);
testConnection();
}
private void testConnection() {
@ -279,11 +283,26 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
@Override
public InputStream sparqlSelectQuery(String queryStr, RDFService.ResultFormat resultFormat) throws RDFServiceException {
Query query = createQuery(queryStr);
QueryExecution qe = QueryExecutionFactory.sparqlService(readEndpointURI, query);
//QueryEngineHTTP qh = new QueryEngineHTTP(readEndpointURI, queryStr);
GetMethod meth = new GetMethod(readEndpointURI);
try {
ResultSet resultSet = qe.execSelect();
meth.addRequestHeader("Accept", "application/sparql-results+xml");
NameValuePair param = new NameValuePair();
param.setName("query");
param.setValue(queryStr);
NameValuePair[] params = new NameValuePair[1];
params[0] = param;
meth.setQueryString(params);
int response = httpClient.executeMethod(meth);
if (response > 399) {
log.error("response " + response + " to query. \n");
log.debug("update string: \n" + queryStr);
throw new RDFServiceException("Unable to perform SPARQL UPDATE");
}
InputStream in = meth.getResponseBodyAsStream();
ResultSet resultSet = ResultSetFactory.fromXML(in);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
switch (resultFormat) {
@ -302,11 +321,13 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
default:
throw new RDFServiceException("unrecognized result format");
}
InputStream result = new ByteArrayInputStream(outputStream.toByteArray());
return result;
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
qe.close();
//qh.close();
meth.releaseConnection();
}
}
@ -474,7 +495,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
int response = httpClient.executeMethod(meth);
if (response > 399) {
log.error("response " + response + " to update. \n");
log.debug("update string: \n" + updateString);
//log.debug("update string: \n" + updateString);
throw new RDFServiceException("Unable to perform SPARQL UPDATE");
}
} finally {
@ -744,6 +765,8 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
private List<Statement> sort(List<Statement> stmts) {
List<Statement> output = new ArrayList<Statement>();
int originalSize = stmts.size();
if (originalSize == 1)
return stmts;
List <Statement> remaining = stmts;
ConcurrentLinkedQueue<com.hp.hpl.jena.rdf.model.Resource> subjQueue =
new ConcurrentLinkedQueue<com.hp.hpl.jena.rdf.model.Resource>();

View file

@ -3,6 +3,7 @@
package edu.cornell.mannlib.vitro.webapp.reasoner;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
@ -295,14 +296,35 @@ public class ABoxRecomputer {
*/
protected Collection<String> getAllIndividualURIs() {
String queryString = "SELECT DISTINCT ?s WHERE { GRAPH ?g { ?s a ?type } " +
" FILTER (!bound(?g) || !regex(str(?g),\"tbox\")) } ORDER BY ?s";
return getIndividualURIs(queryString);
HashSet<String> individualURIs = new HashSet<String>();
List<String> classList = new ArrayList<String>();
tboxModel.enterCriticalSection(Lock.READ);
try {
StmtIterator classIt = tboxModel.listStatements(
(Resource) null, RDF.type, OWL.Class);
while(classIt.hasNext()) {
Statement stmt = classIt.nextStatement();
if(stmt.getSubject().isURIResource()
&& stmt.getSubject().getURI() != null
&& !stmt.getSubject().getURI().isEmpty()) {
classList.add(stmt.getSubject().getURI());
}
}
} finally {
tboxModel.leaveCriticalSection();
}
protected Collection<String> getIndividualURIs(String queryString) {
for (String classURI : classList) {
String queryString = "SELECT ?s WHERE { ?s a <" + classURI + "> } ";
getIndividualURIs(queryString, individualURIs);
}
Set<String> individuals = new HashSet<String>();
return individualURIs;
}
protected void getIndividualURIs(String queryString, Set<String> individuals) {
int batchSize = 50000;
int offset = 0;
@ -342,7 +364,6 @@ public class ABoxRecomputer {
offset += batchSize;
}
return individuals;
}
protected void addedABoxTypeAssertion(Resource individual, Model inferenceModel, HashSet<String> unknownTypes) {
@ -410,6 +431,11 @@ public class ABoxRecomputer {
while (iter.hasNext()) {
Statement stmt = iter.next();
// skip statements with blank nodes to avoid excessive deletion
if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) {
continue;
}
inferenceModel.enterCriticalSection(Lock.WRITE);
try {
inferenceModel.remove(stmt);

View file

@ -10,22 +10,17 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.QuerySolutionMap;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.dao.jena.QueryUtils;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.search.beans.StatementToURIsToUpdate;
/**
@ -38,10 +33,10 @@ import edu.cornell.mannlib.vitro.webapp.search.beans.StatementToURIsToUpdate;
public class AdditionalURIsForObjectProperties implements StatementToURIsToUpdate {
protected static final Log log = LogFactory.getLog(AdditionalURIsForObjectProperties.class);
protected Model model;
protected final RDFService rdfService;
public AdditionalURIsForObjectProperties( Model model){
this.model = model;
public AdditionalURIsForObjectProperties(RDFService rdfService) {
this.rdfService = rdfService;
}
@Override
@ -102,12 +97,9 @@ public class AdditionalURIsForObjectProperties implements StatementToURIsToUpdat
Resource uriResource = ResourceFactory.createResource(uri);
initialBinding.add("uri", uriResource);
Query sparqlQuery = QueryFactory.create( QUERY_FOR_RELATED );
model.getLock().enterCriticalSection(Lock.READ);
try{
QueryExecution qExec = QueryExecutionFactory.create(sparqlQuery, model, initialBinding);
try{
ResultSet results = qExec.execSelect();
ResultSet results = QueryUtils.getQueryResults(QUERY_FOR_RELATED,
initialBinding, rdfService);
while(results.hasNext()){
QuerySolution soln = results.nextSolution();
Iterator<String> iter = soln.varNames() ;
@ -125,14 +117,7 @@ public class AdditionalURIsForObjectProperties implements StatementToURIsToUpdat
}
}
}
}catch(Throwable t){
log.error(t,t);
} finally{
qExec.close();
}
}finally{
model.getLock().leaveCriticalSection();
}
return additionalUris;
}

View file

@ -5,9 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.search.indexing;
import java.util.ArrayList;
import java.util.List;
import com.hp.hpl.jena.ontology.OntModel;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.search.beans.StatementToURIsToUpdate;
/**
@ -16,12 +15,12 @@ import edu.cornell.mannlib.vitro.webapp.search.beans.StatementToURIsToUpdate;
*/
public class AdditionalUriFinders {
public static List<StatementToURIsToUpdate> getList(OntModel jenaOntModel,
public static List<StatementToURIsToUpdate> getList(RDFService rdfService,
IndividualDao indDao) {
// TODO How many of these are only relevant to VIVO?
List<StatementToURIsToUpdate> uriFinders = new ArrayList<>();
uriFinders.add(new AdditionalURIsForDataProperties());
uriFinders.add(new AdditionalURIsForObjectProperties(jenaOntModel));
uriFinders.add(new AdditionalURIsForObjectProperties(rdfService));
uriFinders.add(new AdditionalURIsForTypeStatements());
uriFinders.add(new URIsForClassGroupChange(indDao));
return uriFinders;

View file

@ -23,6 +23,7 @@ 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.dao.jena.ModelContext;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
import edu.cornell.mannlib.vitro.webapp.search.beans.StatementToURIsToUpdate;
@ -133,7 +134,8 @@ public class SolrSetup implements javax.servlet.ServletContextListener{
wadf = new WebappDaoFactoryFiltering(wadf, vf);
// make objects that will find additional URIs for context nodes etc
List<StatementToURIsToUpdate> uriFinders = AdditionalUriFinders.getList(jenaOntModel,wadf.getIndividualDao());
RDFService rdfService = RDFServiceUtils.getRDFServiceFactory(context).getRDFService();
List<StatementToURIsToUpdate> uriFinders = AdditionalUriFinders.getList(rdfService,wadf.getIndividualDao());
// Make the IndexBuilder
IndexBuilder builder = new IndexBuilder( solrIndexer, wadf, uriFinders );

View file

@ -1,44 +1,57 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.search.solr.documentBuilding;
import static edu.cornell.mannlib.vitro.webapp.search.solr.documentBuilding.IndividualToSolrDocument.DONT_EXCLUDE;
import java.util.List;
import org.apache.solr.common.SolrInputDocument;
import org.apache.commons.lang.StringUtils;
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.dao.VitroVocabulary;
/**
* Exclude individuals with types from the Vitro namespace from the
* search index. (Other than old vitro Flag types).
* Exclude individuals with most specific types from the Vitro namespace from
* the search index. (Other than old vitro Flag types).
*/
public class ExcludeNonFlagVitro implements SearchIndexExcluder {
private static final Log log = LogFactory.getLog(ExcludeNonFlagVitro.class);
@Override
public String checkForExclusion(Individual ind) {
if( ind != null && ind.getVClasses() != null ) {
String excludeMsg = skipIfVitro(ind, ind.getVClasses() );
if( excludeMsg != null)
return excludeMsg;
}
return null;
if (ind == null) {
return DONT_EXCLUDE;
}
String skipIfVitro(Individual ind, List<VClass> vclasses) {
for( VClass type: vclasses ){
if( type != null && type.getURI() != null ){
String typeURI = type.getURI();
List<String> mostSpecificTypeUris = ind.getMostSpecificTypeURIs();
if (mostSpecificTypeUris == null) {
return DONT_EXCLUDE;
}
if(typeURI.startsWith( VitroVocabulary.vitroURI )
&& ! typeURI.startsWith(VitroVocabulary.vitroURI + "Flag") ){
String message = skipIfVitro(ind, mostSpecificTypeUris);
if (!StringUtils.equals(DONT_EXCLUDE, message)) {
log.debug("msg=" + message + ", individual=" + ind.getURI() + " ("
+ ind.getLabel() + "), types=" + mostSpecificTypeUris);
}
return message;
}
String skipIfVitro(Individual ind, List<String> mostSpecificTypeUris) {
for (String typeUri : mostSpecificTypeUris) {
if (typeUri == null) {
continue;
}
if (typeUri.startsWith(VitroVocabulary.vitroURI + "Flag")) {
continue;
}
if (typeUri.startsWith(VitroVocabulary.vitroURI)) {
return "Skipped " + ind.getURI() + " because in "
+ VitroVocabulary.vitroURI + " namespace";
}
}
}
return null;
return DONT_EXCLUDE;
}
}

View file

@ -110,6 +110,9 @@ public class IndividualToSolrDocument {
for( SearchIndexExcluder excluder : excludes){
try{
String msg = excluder.checkForExclusion(ind);
log.debug("individual=" + ind.getURI() + " (" + ind.getLabel()
+ "), excluder=" + excluder + ", types="
+ ind.getMostSpecificTypeURIs() + ", msg=" + msg);
if( msg != DONT_EXCLUDE)
return msg;
}catch (Exception e) {

View file

@ -380,6 +380,12 @@ public class FakeApplicationOntologyService {
return dataGetters;
}
@Override
public String toString() {
return "[template=" + templateName + ", dataGetters=" + dataGetters
+ "]";
}
}
/** The view specifications that we read from the config file. */

View file

@ -0,0 +1,47 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.shortview;
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.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
/**
* When we use a short view other than the default, log it.
*/
public class ShortViewLogger {
private static final Log log = LogFactory.getLog(ShortViewLogger.class);
public static void log(VitroRequest vreq, String contextName,
Individual individual, String classUri, TemplateAndDataGetters tdg) {
if (isLogging(vreq)) {
log.info("Using custom short view in " + contextName + " because '"
+ individual.getURI() + "' (" + individual.getLabel()
+ ") has type '" + classUri + "': " + tdg);
}
}
public static void log(VitroRequest vreq, String contextName,
Individual individual) {
if (isLogging(vreq)) {
log.info("Using default short view in " + contextName + " for '"
+ individual.getURI() + "' (" + individual.getLabel() + ")");
}
}
private static boolean isLogging(VitroRequest vreq) {
if (!log.isInfoEnabled()) {
return false;
}
DeveloperSettings settings = DeveloperSettings.getBean(vreq);
return settings.getBoolean(Keys.ENABLED)
&& settings
.getBoolean(Keys.PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW);
}
}

View file

@ -15,9 +15,7 @@ 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.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
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.FreemarkerProcessingService.TemplateProcessingException;
@ -116,11 +114,13 @@ public class ShortViewServiceImpl implements ShortViewService {
TemplateAndDataGetters tdg = faker.getShortViewProperties(vreq,
individual, classUri, svContext.name());
if (tdg != null) {
ShortViewLogger.log(vreq, svContext.name(), individual, classUri, tdg);
return tdg;
}
}
// Didn't find one? Use the default values.
ShortViewLogger.log(vreq, svContext.name(), individual);
return new TemplateAndDataGetters(svContext.getDefaultTemplateName());
}

View file

@ -187,8 +187,7 @@ public class FileGraphSetup implements ServletContextListener {
} else {
baseModel.add(model);
}
log.info("Attached file graph as " + type + " submodel " + p.getFileName());
log.debug("Attached file graph as " + type + " submodel " + p.getFileName());
}
modelChanged = modelChanged | updateGraphInDB(dataset, model, type, p);

View file

@ -221,7 +221,7 @@ public class JenaDataSourceSetupBase extends JenaBaseDaoCon {
int[] maxActiveAndIdle = getMaxActiveAndIdle(ctx);
cpds.setMaxPoolSize(maxActiveAndIdle[0]);
cpds.setMinPoolSize(maxActiveAndIdle[1]);
cpds.setMaxIdleTime(3600); // ms
cpds.setMaxIdleTime(43200); // s
cpds.setMaxIdleTimeExcessConnections(300);
cpds.setAcquireIncrement(5);
cpds.setNumHelperThreads(6);

View file

@ -141,7 +141,7 @@ public class UpdateKnowledgeBase implements ServletContextListener {
KnowledgeBaseUpdater ontologyUpdater = new KnowledgeBaseUpdater(settings);
boolean requiredUpdate = ontologyUpdater.updateRequired(ctx);
if(!JenaDataSourceSetupBase.isFirstStartup()) {
if(requiredUpdate && !JenaDataSourceSetupBase.isFirstStartup()) {
try {
ctx.setAttribute(KBM_REQURIED_AT_STARTUP, Boolean.TRUE);
migrationChangesMade = ontologyUpdater.update(ctx);

View file

@ -0,0 +1,325 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.developer;
import java.io.File;
import java.io.FileReader;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
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.config.ConfigurationProperties;
/**
* Hold the global developer settings. Render to JSON when requested.
*
* On first request, the "developer.properties" file is loaded from the Vitro
* home directory. If the file doesn't exist, or doesn't contain values for
* certain properties, those propertiew will keep their default values.
*
* An AJAX request can be used to update the properties. If the request has
* multiple values for a property, the first value will be used. If the request
* does not contain a value for a property, that property will keep its current
* value.
*/
public class DeveloperSettings {
private static final Log log = LogFactory.getLog(DeveloperSettings.class);
public enum Keys {
/**
* Developer mode and developer panel is enabled.
*/
ENABLED("developer.enabled", true),
/**
* Users don't need authority to use the developer panel. But they still
* can't enable it without authority.
*/
PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true),
/**
* Load Freemarker templates every time they are requested.
*/
DEFEAT_FREEMARKER_CACHE("developer.defeatFreemarkerCache", true),
/**
* Show where each Freemarker template starts and stops.
*/
INSERT_FREEMARKER_DELIMITERS("developer.insertFreemarkerDelimiters",
true),
/**
* Load language property files every time they are requested.
*/
I18N_DEFEAT_CACHE("developer.i18n.defeatCache", true),
/**
* Enable the I18nLogger to log each string request.
*/
I18N_LOG_STRINGS("developer.i18n.logStringRequests", true),
/**
* Enable the LoggingRDFService
*/
LOGGING_RDF_ENABLE("developer.loggingRDFService.enable", true),
/**
* When logging with the LoggingRDFService, include a stack trace
*/
LOGGING_RDF_STACK_TRACE("developer.loggingRDFService.stackTrace", true),
/**
* Don't log with the LoggingRDFService unless the calling stack meets
* this restriction.
*/
LOGGING_RDF_QUERY_RESTRICTION(
"developer.loggingRDFService.queryRestriction", false),
/**
* Don't log with the LoggingRDFService unless the calling stack meets
* this restriction.
*/
LOGGING_RDF_STACK_RESTRICTION(
"developer.loggingRDFService.stackRestriction", false),
/**
* Tell the CustomListViewLogger to note the use of non-default custom
* list views.
*/
PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW(
"developer.pageContents.logCustomListView", true),
/**
* Tell the ShortViewLogger to note the use of non-default short views.
*/
PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW(
"developer.pageContents.logCustomShortView", true);
private final String propertyName;
private final String elementId;
private final boolean bool;
private Keys(String propertyName, boolean bool) {
this.propertyName = propertyName;
this.elementId = produceElementId();
this.bool = bool;
}
public String propertyName() {
return propertyName;
}
public String elementId() {
return elementId;
}
boolean isBoolean() {
return bool;
}
/**
* The element ID is camel-case instead of period-delimited. So
* "developer.enabled" becomes "developerEnabled".
*/
String produceElementId() {
StringBuilder id = new StringBuilder(propertyName.length());
boolean capitalize = false;
for (int i = 0; i < propertyName.length(); i++) {
char c = propertyName.charAt(i);
if (c == '.') {
capitalize = true;
} else if (capitalize) {
id.append(Character.toUpperCase(c));
capitalize = false;
} else {
id.append(c);
}
}
return id.toString();
}
@Override
public String toString() {
return propertyName;
}
static Keys fromElementId(String id) {
for (Keys k : Keys.values()) {
if (k.elementId.equals(id)) {
return k;
}
}
log.error("Can't find key for element id: '" + id + "'");
return null;
}
static Keys fromPropertyName(String name) {
for (Keys k : Keys.values()) {
if (k.propertyName.equals(name)) {
return k;
}
}
log.error("Can't find key for property name: '" + name + "'");
return null;
}
}
// ----------------------------------------------------------------------
// The factory
// ----------------------------------------------------------------------
private static final String ATTRIBUTE_NAME = DeveloperSettings.class
.getName();
public static DeveloperSettings getBean(HttpServletRequest req) {
return getBean(req.getSession().getServletContext());
}
public static DeveloperSettings getBean(ServletContext ctx) {
Object o = ctx.getAttribute(ATTRIBUTE_NAME);
if (o instanceof DeveloperSettings) {
return (DeveloperSettings) o;
} else {
DeveloperSettings ds = new DeveloperSettings(ctx);
ctx.setAttribute(ATTRIBUTE_NAME, ds);
return ds;
}
}
// ----------------------------------------------------------------------
// The instance
// ----------------------------------------------------------------------
private final Map<Keys, Object> settings = new EnumMap<>(Keys.class);
private DeveloperSettings(ServletContext ctx) {
updateFromFile(ctx);
}
/**
* Read the initial settings from "developer.properties" in the Vitro home
* directory.
*
* This method is "protected" so we can override it for unit tests.
*/
protected void updateFromFile(ServletContext ctx) {
Map<Keys, String> fromFile = new HashMap<>();
ConfigurationProperties props = ConfigurationProperties.getBean(ctx);
String home = props.getProperty("vitro.home");
File dsFile = Paths.get(home, "developer.properties").toFile();
if (dsFile.isFile()) {
try (FileReader reader = new FileReader(dsFile)) {
Properties dsProps = new Properties();
dsProps.load(reader);
for (String key : dsProps.stringPropertyNames()) {
fromFile.put(Keys.fromPropertyName(key),
dsProps.getProperty(key));
}
} catch (Exception e) {
log.warn("Failed to load 'developer.properties' file.", e);
}
} else {
log.debug("No developer.properties file.");
}
log.debug("Properties from file: " + fromFile);
update(fromFile);
}
/** Provide the parameter map from the HttpServletRequest */
public void updateFromRequest(Map<String, String[]> parameterMap) {
if (log.isDebugEnabled()) {
dumpParameterMap(parameterMap);
}
Map<Keys, String> fromRequest = new HashMap<>();
for (String key : parameterMap.keySet()) {
fromRequest.put(Keys.fromElementId(key), parameterMap.get(key)[0]);
}
update(fromRequest);
}
private void update(Map<Keys, String> changedSettings) {
for (Keys key : Keys.values()) {
String s = changedSettings.get(key);
if (s != null) {
if (key.isBoolean()) {
settings.put(key, Boolean.valueOf(s));
} else {
settings.put(key, s);
}
}
}
log.debug("DeveloperSettings: " + this);
}
public Object get(Keys key) {
if (key.isBoolean()) {
return getBoolean(key);
} else {
return getString(key);
}
}
public boolean getBoolean(Keys key) {
if (!key.isBoolean()) {
throw new IllegalArgumentException("Key '" + key
+ "' does not take a boolean value.");
}
if (settings.containsKey(key)) {
if (Boolean.TRUE.equals(settings.get(Keys.ENABLED))) {
return (Boolean) settings.get(key);
}
}
return false;
}
public String getString(Keys key) {
if (key.isBoolean()) {
throw new IllegalArgumentException("Key '" + key
+ "' takes a boolean value.");
}
if (settings.containsKey(key)) {
if (Boolean.TRUE.equals(settings.get(Keys.ENABLED))) {
return (String) settings.get(key);
}
}
return "";
}
public Map<String, Object> getSettingsMap() {
Map<String, Object> map = new HashMap<>();
for (Keys key : Keys.values()) {
map.put(key.elementId(), get(key));
}
return map;
}
@Override
public String toString() {
return "DeveloperSettings" + settings;
}
/* For debugging. */
private void dumpParameterMap(Map<String, String[]> parameterMap) {
Map<String, List<String>> map = new HashMap<>();
for (String key : parameterMap.keySet()) {
map.put(key, Arrays.asList(parameterMap.get(key)));
}
log.debug("Parameter map: " + map);
}
}

View file

@ -0,0 +1,93 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.developer;
import static edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys.PERMIT_ANONYMOUS_CONTROL;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.ajax.VitroAjaxController;
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingService.TemplateProcessingException;
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingServiceSetup;
/**
* Accept an AJAX request to update the developer settings. Return an HTML
* representation of the developer panel from the settings and a Freemarker
* template.
*
* If developer mode is not enabled, the HTML response is empty.
*
* You may only control the panel if you are logged in with sufficient
* authorization, or if anonymous control is permitted by the settings.
*
* If you are not allowed to control the panel, then the HTML response
* is only a statement that developer mode is enabled. Otherwise, it
* is a full panel (collapsed at first).
*/
public class DeveloperSettingsServlet extends VitroAjaxController {
private static final Log log = LogFactory
.getLog(DeveloperSettingsServlet.class);
@Override
protected void doRequest(VitroRequest vreq, HttpServletResponse resp)
throws ServletException, IOException {
DeveloperSettings settings = DeveloperSettings.getBean(vreq);
/*
* Are they allowed to control the panel?
*/
if (isAuthorized(vreq)) {
// Update the settings.
settings.updateFromRequest(vreq.getParameterMap());
} else {
log.debug("Not authorized to update settings.");
}
/*
* Build the response.
*/
try {
Map<String, Object> bodyMap = buildBodyMap(isAuthorized(vreq),
settings);
String rendered = renderTemplate(vreq, bodyMap);
resp.getWriter().write(rendered);
} catch (Exception e) {
doError(resp, e.toString(), 500);
}
}
private Map<String, Object> buildBodyMap(boolean authorized,
DeveloperSettings settings) {
Map<String, Object> settingsMap = new HashMap<>();
settingsMap.putAll(settings.getSettingsMap());
settingsMap.put("mayControl", authorized);
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("settings", settingsMap);
return bodyMap;
}
private String renderTemplate(VitroRequest vreq, Map<String, Object> bodyMap)
throws TemplateProcessingException {
return FreemarkerProcessingServiceSetup.getService(getServletContext())
.renderTemplate("developerPanel.ftl", bodyMap, vreq);
}
private boolean isAuthorized(VitroRequest vreq) {
boolean authBySetting = DeveloperSettings.getBean(vreq).getBoolean(
PERMIT_ANONYMOUS_CONTROL);
boolean authByPolicy = PolicyHelper.isAuthorizedForActions(vreq,
SimplePermission.ENABLE_DEVELOPER_PANEL.ACTION);
return authBySetting || authByPolicy;
}
}

View file

@ -0,0 +1,38 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.customlistview;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys;
/**
* If enabled in the developer settings (and log levels), log every non-default
* custom list view.
*/
public class CustomListViewLogger {
private static final Log log = LogFactory
.getLog(CustomListViewLogger.class);
public static void log(VitroRequest vreq, ObjectProperty op,
String configFileName) {
if (isLogging(vreq)) {
log.info("Using list view: '" + configFileName + "' for "
+ op.getURI() + " (" + op.getLabel() + ")");
}
}
private static boolean isLogging(VitroRequest vreq) {
if (!log.isInfoEnabled()) {
return false;
}
DeveloperSettings settings = DeveloperSettings.getBean(vreq);
return settings.getBoolean(Keys.ENABLED)
&& settings.getBoolean(Keys.PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW);
}
}

View file

@ -61,6 +61,8 @@ public class PropertyListConfig {
String configFileName = wadf.getObjectPropertyDao().getCustomListViewConfigFileName(op);
if (configFileName == null) { // no custom config; use default config
configFileName = DEFAULT_CONFIG_FILE_NAME;
} else {
CustomListViewLogger.log(vreq, op, configFileName);
}
log.debug("Using list view config file " + configFileName + " for object property " + op.getURI());

View file

@ -491,6 +491,10 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
return getDataLiteralValuesFromParameter();
}
//Get a custom object uri for deletion if one exists, i.e. not the object uri for the property
public String getCustomDeleteObjectUri() {
return (String) vreq.getParameter("deleteObjectUri");
}
//Used for deletion in case there's a specific template to be employed
public String getDeleteTemplate() {
String templateName = vreq.getParameter("templateName");
@ -544,7 +548,7 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
}
for(VClass rangeVClass : rangeVClasses) {
vclasses.add(rangeVClass);
List<String> subURIs = wdf.getVClassDao().getSubClassURIs(rangeVClass.getURI());
List<String> subURIs = wdf.getVClassDao().getAllSubClassURIs(rangeVClass.getURI());
for (String subClassURI : subURIs) {
VClass subClass = wdf.getVClassDao().getVClassByURI(subClassURI);
if (subClass != null) {

View file

@ -9,6 +9,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayDataProperty;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayObjectProperty;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
@ -50,8 +51,15 @@ public class PropertyGroupTemplateModel extends BaseTemplateModel {
properties.add(tm);
}
} else if (p instanceof DataProperty){
DataProperty dp = (DataProperty) p;
RequestedAction dop = new DisplayDataProperty(dp);
if (!PolicyHelper.isAuthorizedForActions(vreq, dop)) {
continue;
}
properties.add(new DataPropertyTemplateModel(dp, subject, vreq, editing, populatedDataPropertyList));
} else {
properties.add(new DataPropertyTemplateModel((DataProperty)p, subject, vreq, editing, populatedDataPropertyList));
log.debug(p.getURI() + " is neither an ObjectProperty nor a DataProperty; skipping display");
}
}
}

View file

@ -11,11 +11,14 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.Property;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel;
@ -94,6 +97,27 @@ public abstract class PropertyTemplateModel extends BaseTemplateModel {
String editUrl = UrlBuilder.getUrl(getPropertyEditRoute(), "uri", property.getURI());
verboseDisplay.put("propertyEditUrl", editUrl);
if(isFauxProperty(property)) {
verboseDisplay.put("fauxProperty", "true");
}
}
private boolean isFauxProperty(Property prop) {
if(!(prop instanceof ObjectProperty)) {
return false;
}
ObjectPropertyDao opDao = vreq.getWebappDaoFactory().getObjectPropertyDao();
ObjectProperty baseProp = opDao.getObjectPropertyByURI(prop.getURI());
if(baseProp == null) {
return false;
}
ObjectProperty possibleFaux = (ObjectProperty) prop;
if (possibleFaux.getDomainPublic() == null) {
return (baseProp.getDomainPublic() != null);
} else {
return !possibleFaux.getDomainPublic().equals(baseProp.getDomainPublic());
}
}
protected abstract int getPropertyDisplayTier(Property p);

View file

@ -1,183 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.text.SimpleDateFormat;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
/**
* Test the methods of {@link FlatteningTemplateLoader}.
*/
public class FlatteningTemplateLoaderTest extends AbstractTestClass {
/**
* TODO test plan
*
* <pre>
* findTemplateSource
* null arg
* not found
* found in top level
* found in lower level
* with path
*
* getReader
* get it, read it, check it, close it.
*
* getLastModified
* check the create date within a range
* modify it and check again.
*
* </pre>
*/
// ----------------------------------------------------------------------
// setup and teardown
// ----------------------------------------------------------------------
private static final String SUBDIRECTORY_NAME = "sub";
private static final String TEMPLATE_NAME_UPPER = "template.ftl";
private static final String TEMPLATE_NAME_UPPER_WITH_PATH = "path/template.ftl";
private static final String TEMPLATE_UPPER_CONTENTS = "The contents of the file.";
private static final String TEMPLATE_NAME_LOWER = "another.ftl";
private static final String TEMPLATE_LOWER_CONTENTS = "Another template file.";
private static File tempDir;
private static File notADirectory;
private static File upperTemplate;
private static File lowerTemplate;
private FlatteningTemplateLoader loader;
@BeforeClass
public static void setUpFiles() throws IOException {
notADirectory = File.createTempFile(
FlatteningTemplateLoader.class.getSimpleName(), "");
tempDir = createTempDirectory(FlatteningTemplateLoader.class
.getSimpleName());
upperTemplate = createFile(tempDir, TEMPLATE_NAME_UPPER,
TEMPLATE_UPPER_CONTENTS);
File subdirectory = new File(tempDir, SUBDIRECTORY_NAME);
subdirectory.mkdir();
lowerTemplate = createFile(subdirectory, TEMPLATE_NAME_LOWER,
TEMPLATE_LOWER_CONTENTS);
}
@Before
public void initializeLoader() {
loader = new FlatteningTemplateLoader(tempDir);
}
@AfterClass
public static void cleanUpFiles() throws IOException {
purgeDirectoryRecursively(tempDir);
}
// ----------------------------------------------------------------------
// the tests
// ----------------------------------------------------------------------
@Test(expected = NullPointerException.class)
public void constructorNull() {
new FlatteningTemplateLoader(null);
}
@Test(expected = IllegalArgumentException.class)
public void constructorNonExistent() {
new FlatteningTemplateLoader(new File("bogusDirName"));
}
@Test(expected = IllegalArgumentException.class)
public void constructorNotADirectory() {
new FlatteningTemplateLoader(notADirectory);
}
@Test
public void findNull() throws IOException {
Object source = loader.findTemplateSource(null);
assertNull("find null", source);
}
@Test
public void findNotFound() throws IOException {
Object source = loader.findTemplateSource("bogus");
assertNull("not found", source);
}
@Test
public void findInTopLevel() throws IOException {
Object source = loader.findTemplateSource(TEMPLATE_NAME_UPPER);
assertEquals("top level", upperTemplate, source);
}
@Test
public void findInLowerLevel() throws IOException {
Object source = loader.findTemplateSource(TEMPLATE_NAME_LOWER);
assertEquals("lower level", lowerTemplate, source);
}
@Test
public void findIgnoringPath() throws IOException {
Object source = loader
.findTemplateSource(TEMPLATE_NAME_UPPER_WITH_PATH);
assertEquals("top level", upperTemplate, source);
}
@Test
public void checkTheReader() throws IOException {
Object source = loader.findTemplateSource(TEMPLATE_NAME_UPPER);
Reader reader = loader.getReader(source, "UTF-8");
String contents = readAll(reader);
assertEquals("read the contents", contents, TEMPLATE_UPPER_CONTENTS);
}
/**
* Some systems only record last-modified times to the nearest second, so we
* can't rely on them changing during the course of the test. Force the
* change, and test for it.
*/
@Test
public void teplateLastModified() throws IOException {
Object source = loader.findTemplateSource(TEMPLATE_NAME_UPPER);
long modified = loader.getLastModified(source);
long now = System.currentTimeMillis();
assertTrue("near to now: modified=" + formatTimeStamp(modified)
+ ", now=" + formatTimeStamp(now),
2000 > Math.abs(modified - now));
upperTemplate.setLastModified(5000);
assertEquals("modified modified", 5000, loader.getLastModified(source));
}
@Test
public void closeDoesntCrash() throws IOException {
Object source = loader.findTemplateSource(TEMPLATE_NAME_UPPER);
loader.closeTemplateSource(source);
}
// ----------------------------------------------------------------------
// helper methods
// ----------------------------------------------------------------------
private String formatTimeStamp(long time) {
SimpleDateFormat formatter = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS");
return formatter.format(time);
}
}

View file

@ -0,0 +1,65 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena;
import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
import com.hp.hpl.jena.query.QuerySolutionMap;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
/**
* TODO
*/
public class QueryUtilsTest extends AbstractTestClass {
private QuerySolutionMap bindings = new QuerySolutionMap();
@Test
public void bindResource() {
bindings.add("uri", ResourceFactory.createResource("http://my.uri"));
assertBoundQueryEquals("a resource ?uri", "a resource <http://my.uri>");
}
@Test
public void bindPlainLiteral() {
bindings.add("plain", ResourceFactory.createPlainLiteral("too easy"));
assertBoundQueryEquals("This is ?plain ?plain",
"This is \"too easy\" \"too easy\"");
}
@Test
public void bindTypedLiteral() {
bindings.add("typed", ResourceFactory.createTypedLiteral(100L));
assertBoundQueryEquals("take this ?typed number",
"take this \"100\"^^<http://www.w3.org/2001/XMLSchema#long> number");
}
@Test
public void bindLanguageLiteral() {
Literal l = ModelFactory.createDefaultModel().createLiteral("Spanish",
"es-ES");
bindings.add("lang", l);
assertBoundQueryEquals("speak my ?lang?", "speak my \"Spanish\"@es-ES?");
}
@Ignore
@Test
public void bindAnon() {
fail("bindAnon not implemented");
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void assertBoundQueryEquals(String template, String expected) {
String actual = QueryUtils.bindVariables(template, bindings);
assertEquals("bounding results", expected, actual);
}
}

View file

@ -0,0 +1,360 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.freemarker.loader;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.SortedSet;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import edu.cornell.mannlib.vitro.webapp.freemarker.loader.FreemarkerTemplateLoader.PathPieces;
import edu.cornell.mannlib.vitro.webapp.freemarker.loader.FreemarkerTemplateLoader.PathPiecesFileVisitor;
/**
* TODO
*/
public class FreemarkerTemplateLoaderTest {
private PathPiecesFileVisitor visitor;
private String[] paths;
// ----------------------------------------------------------------------
// PathPieces tests
// ----------------------------------------------------------------------
@Test
public void ppLanguageRegionExtension() {
assertPathPieces("this_en_US.ftl", "this", "_en", "_US", ".ftl");
}
@Test
public void ppLanguageRegion() {
assertPathPieces("this_en_US", "this", "_en", "_US", "");
}
@Test
public void ppLanguageExtension() {
assertPathPieces("this_en.ftl", "this", "_en", "", ".ftl");
}
@Test
public void ppLanguage() {
assertPathPieces("this_en", "this", "_en", "", "");
}
@Test
public void ppDefaultExtension() {
assertPathPieces("this.ftl", "this", "", "", ".ftl");
}
@Test
public void ppDefault() {
assertPathPieces("this", "this", "", "", "");
}
@Test
public void ppExtraUnderscoreExtension() {
assertPathPieces("woo_hoo_en_US.ftl", "woo_hoo", "_en", "_US", ".ftl");
}
@Test
public void ppExtraUnderscore() {
assertPathPieces("woo_hoo_en_US", "woo_hoo", "_en", "_US", "");
}
// ----------------------------------------------------------------------
// Specific function tests
// ----------------------------------------------------------------------
@Test
public void baseAndExtensionMatch() {
paths("match-me.ftl");
assertMatches("match-me.ftl", 1, "match-me.ftl");
}
@Test
public void baseAndExtensionDontMatch() {
paths("match-me.ftl");
assertMatches("fail.ftl", 0, null);
assertMatches("match-me", 0, null);
assertMatches("match-me.FTL", 0, null);
}
@Test
public void matchRegardlessOfDepth() {
paths("short-path.ftl", "long/long-path.ftl");
assertMatches("long/short-path.ftl", 1, "short-path.ftl");
assertMatches("long-path.ftl", 1, "long/long-path.ftl");
}
@Test
public void preferShorterPath() {
paths("shorter-is-better", "long/shorter-is-better");
assertMatches("shorter-is-better", 2, "shorter-is-better");
}
@Test
public void preferShorterPathToExactPath() {
paths("shorter-is-better", "long/shorter-is-better");
assertMatches("long/shorter-is-better", 2, "shorter-is-better");
}
@Test
public void languageAndRegionMustMatchExactly() {
paths("this_es_MX.ftl", "this_es_ES.ftl", "this_es.ftl");
assertMatches("this_es_ES.ftl", 1, "this_es_ES.ftl");
}
@Test
public void languageAndRegionNoMatch() {
paths("this_es_MX.ftl", "this_es_ES.ftl", "this_es.ftl");
assertMatches("this_es_GO.ftl", 0, null);
}
@Test
public void languagePrefersExactMatch() {
paths("this_es_MX.ftl", "this_es.ftl", "this_es_ES.ftl");
assertMatches("this_es.ftl", 3, "this_es.ftl");
}
@Test
public void languageAcceptsApproximateMatch() {
paths("this_es_MX.ftl");
assertMatches("this_es.ftl", 1, "this_es_MX.ftl");
}
@Test
public void languagePrefersApproximateAlphabetical() {
paths("this_es_MX.ftl", "this_es_ES.ftl");
assertMatches("this_es.ftl", 2, "this_es_ES.ftl");
}
@Test
public void defaultPrefersExactMatch() {
paths("this_fr.ftl", "this.ftl", "this_fr_BE.ftl");
assertMatches("this.ftl", 3, "this.ftl");
}
@Test
public void defaultPrefersDefaultRegion() {
paths("this_fr_BE.ftl", "this_fr.ftl", "this_fr_CA.ftl");
assertMatches("this.ftl", 3, "this_fr.ftl");
}
@Test
public void defaultPrefersLanguageAlphabetical() {
paths("this_es.ftl", "this_fr.ftl");
assertMatches("this.ftl", 2, "this_es.ftl");
}
@Test
public void defaultPrefersRegionAlphabetical() {
paths("this_fr_BE.ftl", "this_fr_CA.ftl");
assertMatches("this.ftl", 2, "this_fr_BE.ftl");
}
// ----------------------------------------------------------------------
// Freemarker simulation tests
// ----------------------------------------------------------------------
public static final String[] FREEMARKER_TEST_PATHS = {
"long/this_fr_BE.ftl", "language_fr.ftl", "default.ftl",
"language-approx_en_US.ftl" };
@Test
public void freemarkerLangAndRegionExact() {
paths = FREEMARKER_TEST_PATHS;
assertFM("this_fr_BE.ftl", 1, "long/this_fr_BE.ftl");
}
@Test
public void freemarkerLangAndRegionMatchLang() {
paths = FREEMARKER_TEST_PATHS;
assertFM("language_fr_CA.ftl", 2, "language_fr.ftl");
}
@Test
public void freemarkerLangAndRegionMatchDefault() {
paths = FREEMARKER_TEST_PATHS;
assertFM("default_es_ES.ftl", 3, "default.ftl");
}
@Test
public void freemarkerLangAndRegionNoMatch() {
paths = FREEMARKER_TEST_PATHS;
assertFM("bogus_en_US.ftl", 3, null);
}
@Test
public void freemarkerLangExact() {
paths = FREEMARKER_TEST_PATHS;
assertFM("language_fr.ftl", 1, "language_fr.ftl");
}
@Test
public void freemarkerLangMatchLangAndRegion() {
paths = FREEMARKER_TEST_PATHS;
assertFM("language-approx_en.ftl", 1, "language-approx_en_US.ftl");
}
@Test
public void freemarkerLangMatchDefault() {
paths = FREEMARKER_TEST_PATHS;
assertFM("default_en.ftl", 2, "default.ftl");
}
@Test
public void freemarkerLangNoMatch() {
paths = FREEMARKER_TEST_PATHS;
assertFM("bogus_it.ftl", 2, null);
}
@Test
public void freemarkerDefaultExact() {
paths = FREEMARKER_TEST_PATHS;
assertFM("default.ftl", 1, "default.ftl");
}
@Test
public void freemarkerDefaultMatchLang() {
paths = FREEMARKER_TEST_PATHS;
assertFM("language.ftl", 1, "language_fr.ftl");
}
@Test
public void freemarkerDefaultMatchLangAndRegion() {
paths = FREEMARKER_TEST_PATHS;
assertFM("this.ftl", 1, "long/this_fr_BE.ftl");
}
@Test
public void freemarkerDefaultNoMatch() {
paths = FREEMARKER_TEST_PATHS;
assertFM("bogus.ftl", 1, null);
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void paths(String... p) {
this.paths = p;
}
private void assertPathPieces(String path, String base, String language,
String region, String extension) {
PathPieces pp = new PathPieces(path);
String[] expected = new String[] { base, language, region, extension };
String[] actual = new String[] { pp.base, pp.language, pp.region,
pp.extension };
assertEquals("pieces", Arrays.asList(expected), Arrays.asList(actual));
}
/**
* @param searchTerm
* template we are looking for
* @param expectedHowMany
* How many matches do we expect?
* @param expectedBestFit
* What should the best match turn out to be?
* @throws IOException
*/
private void assertMatches(String searchTerm, int expectedHowMany,
String expectedBestFitString) {
Path expectedBestFit = (expectedBestFitString == null) ? null : Paths
.get(expectedBestFitString);
SortedSet<PathPieces> matches = runTheVisitor(searchTerm);
int actualHowMany = matches.size();
Path actualBestFit = matches.isEmpty() ? null : matches.last().path;
if (expectedHowMany != actualHowMany) {
fail("How many results: expected " + expectedHowMany
+ ", but was " + actualHowMany + ": " + matches);
}
assertEquals("Best result", expectedBestFit, actualBestFit);
}
/**
* Try for exact match, then pare down if needed, just like Freemarker
* would.
*/
private void assertFM(String searchTerm, int expectedNumberOfTries,
String expectedBestString) {
Path expectedBestFit = expectedBestString == null ? null : Paths
.get(expectedBestString);
PathPieces stPp = new PathPieces(searchTerm);
int actualNumberOfTries = 0;
Path actualBestFit = null;
if (StringUtils.isNotBlank(stPp.region)) {
actualNumberOfTries++;
SortedSet<PathPieces> matches = runTheVisitor(stPp.base
+ stPp.language + stPp.region + stPp.extension);
if (!matches.isEmpty()) {
actualBestFit = matches.last().path;
}
}
if (actualBestFit == null && StringUtils.isNotBlank(stPp.language)) {
actualNumberOfTries++;
SortedSet<PathPieces> matches = runTheVisitor(stPp.base
+ stPp.language + stPp.extension);
if (!matches.isEmpty()) {
actualBestFit = matches.last().path;
}
}
if (actualBestFit == null) {
actualNumberOfTries++;
SortedSet<PathPieces> matches = runTheVisitor(stPp.base
+ stPp.extension);
if (!matches.isEmpty()) {
actualBestFit = matches.last().path;
}
}
assertEquals("How many tries", expectedNumberOfTries,
actualNumberOfTries);
assertEquals("best fit", expectedBestFit, actualBestFit);
}
private SortedSet<PathPieces> runTheVisitor(String searchTerm) {
try {
visitor = new PathPiecesFileVisitorStub(new PathPieces(searchTerm));
for (String p : this.paths) {
visitor.visitFile(Paths.get(p), null);
}
} catch (IOException e) {
fail("Failed: " + e);
}
return visitor.getMatches();
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
/**
* We want to test the PathPiecesFileVisitor, but we can't have it checking
* to see whether the files actually exist.
*/
private static class PathPiecesFileVisitorStub extends
PathPiecesFileVisitor {
public PathPiecesFileVisitorStub(PathPieces searchTerm) {
super(searchTerm);
}
@Override
public boolean fileQualifies(Path path) {
return true;
}
}
}

View file

@ -0,0 +1,93 @@
package edu.cornell.mannlib.vitro.webapp.i18n;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
import stubs.javax.servlet.ServletContextStub;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale;
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/**
* Test the I18N functionality.
*
* Start by checking the logic that finds approximate matches for
* language-specific property files.
*/
public class I18nTest extends AbstractTestClass {
private static final List<Locale> SELECTABLE_LOCALES = locales("es_MX",
"en_US");
ServletContextStub ctx;
@Before
public void setup() {
ctx = new ServletContextStub();
}
@Test
public void noMatchOnLanguageRegion() {
assertLocales("fr_CA", SELECTABLE_LOCALES, "fr_CA", "fr", "");
}
@Test
public void noMatchOnLanguage() {
assertLocales("fr", SELECTABLE_LOCALES, "fr", "");
}
@Test
public void noMatchOnRoot() {
assertLocales("", SELECTABLE_LOCALES, "");
}
@Test
public void matchOnLanguageRegion() {
assertLocales("es_ES", SELECTABLE_LOCALES, "es_ES", "es", "es_MX", "");
}
@Test
public void matchOnLanguage() {
assertLocales("es", SELECTABLE_LOCALES, "es", "es_MX", "");
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void assertLocales(String requested, List<Locale> selectable,
String... expected) {
SelectedLocale.setSelectableLocales(ctx, selectable);
List<Locale> expectedLocales = locales(expected);
I18n.ThemeBasedControl control = new I18n.ThemeBasedControl(ctx,
"bogusThemeDirectory");
List<Locale> actualLocales = control.getCandidateLocales(
"bogusBaseName", locale(requested));
assertEquals("Expected locales", expectedLocales, actualLocales);
}
private static List<Locale> locales(String... strings) {
List<Locale> locales = new ArrayList<>();
for (String s : strings) {
locales.add(locale(s));
}
return locales;
}
private static Locale locale(String s) {
String[] parts = s.split("_");
String language = (parts.length > 0) ? parts[0] : "";
String country = (parts.length > 1) ? parts[1] : "";
String variant = (parts.length > 2) ? parts[2] : "";
return new Locale(language, country, variant);
}
}

View file

@ -15,9 +15,13 @@ import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel;
public class AdditionalURIsForObjectPropertiesTest {
Model model;
RDFService rdfService;
String testNS = "http://example.com/test#";
String n3 = "" +
@ -39,11 +43,12 @@ public class AdditionalURIsForObjectPropertiesTest {
public void setUp() throws Exception {
model = ModelFactory.createDefaultModel();
model.read(new StringReader(n3 ), null , "N3");
rdfService = new RDFServiceModel(model);
}
@Test
public void testChangeOfRdfsLabel() {
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model);
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(rdfService);
List<String> uris = aufop.findAdditionalURIsToIndex(
ResourceFactory.createStatement(
ResourceFactory.createResource(testNS + "bob"),
@ -66,7 +71,7 @@ public class AdditionalURIsForObjectPropertiesTest {
@Test
public void testChangeOfObjPropStmt() {
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model);
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(rdfService);
List<String> uris = aufop.findAdditionalURIsToIndex(
ResourceFactory.createStatement(
ResourceFactory.createResource(testNS + "bob"),
@ -88,7 +93,7 @@ public class AdditionalURIsForObjectPropertiesTest {
@Test
public void testOfDataPropChange() {
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model);
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(rdfService);
List<String> uris = aufop.findAdditionalURIsToIndex(
ResourceFactory.createStatement(
ResourceFactory.createResource(testNS + "bob"),
@ -107,8 +112,9 @@ public class AdditionalURIsForObjectPropertiesTest {
Model model = ModelFactory.createDefaultModel();
model.read(new StringReader( n3ForNIHVIVO_2902 ), null , "N3");
RDFService rdfService = new RDFServiceModel(model);
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model);
AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(rdfService);
List<String> uris = aufop.findAdditionalURIsToIndex(
ResourceFactory.createStatement(
ResourceFactory.createResource("http://caruso-laptop.mannlib.cornell.edu:8090/vivo/individual/n2241"),

View file

@ -51,7 +51,7 @@ public class I18nStub extends I18n {
private class I18nBundleStub extends I18nBundle {
public I18nBundleStub(String bundleName) {
super(bundleName, new DummyResourceBundle());
super(bundleName, new DummyResourceBundle(), null);
}
@Override

View file

@ -2,6 +2,8 @@
</header>
<#include "developer.ftl">
<nav role="navigation">
<ul id="main-nav" role="list">
<#list menu.items as item>

View file

@ -814,6 +814,15 @@
<url-pattern>/searchHelp</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DeveloperAjax</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettingsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DeveloperAjax</servlet-name>
<url-pattern>/admin/developerAjax</url-pattern>
</servlet-mapping>
<!-- for now, need to make sure the links on CALS' site doesn't break -->
<servlet-mapping>
<servlet-name>SearchController</servlet-name>

View file

@ -0,0 +1,48 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/* Styles for the developer panel. */
div.developer {
background-color: #f7dd8a;
padding: 0px 10px 0px 10px;
font-variant: small-caps;
}
div.developer #developerPanelBody {
display: none;
line-height: 1em;
font-size: small;
}
div.developer div.devleft {
width: 49%
}
div.developer div.devright {
float: right;
width: 49%
}
div.developer div.container {
border: thin groove black;
padding: 3px 10px 3px 10px;
margin: 3px 0px 3px 0px;
}
div.developer div.within {
padding-left: 1.5em;
}
div.developer input[type="text"] {
padding: 2px 10px 2px 10px;
line-height: 1em;
margin: 2px 2px 2px 2px;
}
div.developer input[type="text"]:disabled {
background-color: #f8eeae;
}
div.developer a {
margin: 3px;
}

View file

@ -456,7 +456,7 @@ please_create = Please create
a_classgroup = a class group
associate_classes_with_group = and associate classes with the group created.
refresh_content = Refresh Content
site_maintenance = Site Maintenance
rebuild_search_index = Rebuild search index
rebuild_vis_cache = Rebuild visualization cache
recompute_inferences_mixed_caps = Recompute inferences
@ -493,6 +493,9 @@ restrict_logins_mixed_caps = Restrict logins
site_information = Site information
user_accounts = User accounts
activate_developer_panel = Activate developer panel
activate_developer_panel_mixed_caps = Activate developer panel
#
# search controller ( PagedSearchController.java )
#

View file

@ -0,0 +1,88 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
function DeveloperPanel(developerAjaxUrl) {
this.setupDeveloperPanel = updateDeveloperPanel;
function updateDeveloperPanel(data) {
$.ajax({
url: developerAjaxUrl,
dataType: "json",
data: data,
complete: function(xhr, status) {
updatePanelContents(xhr.responseText);
if (document.getElementById("developerPanelSaveButton")) {
enablePanelOpener();
addBehaviorToElements();
updateDisabledFields();
}
}
});
}
function updatePanelContents(contents) {
document.getElementById("developerPanel").innerHTML = contents;
}
function enablePanelOpener() {
document.getElementById("developerPanelClickMe").onclick = function() {
document.getElementById("developerPanelClickText").style.display = "none";
document.getElementById("developerPanelBody").style.display = "block";
};
}
function addBehaviorToElements() {
document.getElementById("developerPanelSaveButton").onclick = function() {
updateDeveloperPanel(collectFormData());
}
document.getElementById("developerEnabled").onchange = updateDisabledFields
document.getElementById("developerLoggingRDFServiceEnable").onchange = updateDisabledFields
}
function updateDisabledFields() {
var developerEnabled = document.getElementById("developerEnabled").checked;
document.getElementById("developerDefeatFreemarkerCache").disabled = !developerEnabled;
document.getElementById("developerInsertFreemarkerDelimiters").disabled = !developerEnabled;
document.getElementById("developerPageContentsLogCustomListView").disabled = !developerEnabled;
document.getElementById("developerPageContentsLogCustomShortView").disabled = !developerEnabled;
document.getElementById("developerI18nDefeatCache").disabled = !developerEnabled;
document.getElementById("developerI18nLogStringRequests").disabled = !developerEnabled;
document.getElementById("developerLoggingRDFServiceEnable").disabled = !developerEnabled;
var rdfServiceEnabled = developerEnabled && document.getElementById("developerLoggingRDFServiceEnable").checked;
document.getElementById("developerLoggingRDFServiceStackTrace").disabled = !rdfServiceEnabled;
document.getElementById("developerLoggingRDFServiceQueryRestriction").disabled = !rdfServiceEnabled;
document.getElementById("developerLoggingRDFServiceStackRestriction").disabled = !rdfServiceEnabled;
}
function collectFormData() {
var data = new Object();
getCheckbox("developerEnabled", data);
getCheckbox("developerDefeatFreemarkerCache", data);
getCheckbox("developerInsertFreemarkerDelimiters", data);
getCheckbox("developerPageContentsLogCustomListView", data);
getCheckbox("developerPageContentsLogCustomShortView", data);
getCheckbox("developerI18nDefeatCache", data);
getCheckbox("developerI18nLogStringRequests", data);
getCheckbox("developerLoggingRDFServiceEnable", data);
getCheckbox("developerLoggingRDFServiceStackTrace", data);
getText("developerLoggingRDFServiceQueryRestriction", data);
getText("developerLoggingRDFServiceStackRestriction", data);
return data;
}
function getCheckbox(key, dest) {
dest[key] = document.getElementById(key).checked;
}
function getText(key, dest) {
dest[key] = document.getElementById(key).value;
}
}
/*
* Relies on the global variable for the AJAX URL.
*/
$(document).ready(function() {
new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({});
});

View file

@ -1,23 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Template for the main Site Administration page -->
<#if indexCacheRebuild?has_content>
<section class="pageBodyGroup indexCacheRebuild" role="region">
<h3>${i18n().refresh_content}</h3>
<ul role="navigation">
<#if indexCacheRebuild.rebuildSearchIndex?has_content>
<li role="listitem"><a href="${indexCacheRebuild.rebuildSearchIndex }" title="${i18n().rebuild_search_index}">${i18n().rebuild_search_index}</a></li>
</#if>
<#if indexCacheRebuild.rebuildVisCache?has_content>
<li role="listitem"><a href="${indexCacheRebuild.rebuildVisCache}" title="${i18n().rebuild_vis_cache}">${i18n().rebuild_vis_cache}</a></li>
</#if>
<#if indexCacheRebuild.recomputeInferences?has_content>
<li role="listitem"><a href="${indexCacheRebuild.recomputeInferences}" title="${i18n().recompute_inferences}">${i18n().recompute_inferences_mixed_caps}</a></li>
</#if>
</ul>
</section>
</#if>

View file

@ -13,5 +13,5 @@ ${stylesheets.add('<link rel="stylesheet" href="${urls.base}/css/admin.css" />')
<#include "siteAdmin-siteConfiguration.ftl">
<#include "siteAdmin-ontologyEditor.ftl">
<#include "siteAdmin-advancedDataTools.ftl">
<#include "siteAdmin-indexCacheRebuild.ftl">
<#include "siteAdmin-siteMaintenance.ftl">
</div>

View file

@ -23,10 +23,6 @@
<li role="listitem"><a href="${siteConfig.menuManagement}" title="${i18n().menu_ordering}">${i18n().menu_ordering_mixed_caps}</a></li>
</#if>
<#if siteConfig.restrictLogins?has_content>
<li role="listitem"><a href="${siteConfig.restrictLogins}" title="${i18n().restrict_logins}">${i18n().restrict_logins_mixed_caps}</a></li>
</#if>
<#if siteConfig.siteInfo?has_content>
<li role="listitem"><a href="${siteConfig.siteInfo}" title="${i18n().site_information}">${i18n().site_information}</a></li>
</#if>

View file

@ -0,0 +1,31 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Template for the main Site Administration page -->
<#if siteMaintenance?has_content>
<section class="pageBodyGroup" role="region">
<h3>${i18n().site_maintenance}</h3>
<ul role="navigation">
<#if siteMaintenance.rebuildSearchIndex?has_content>
<li role="listitem"><a href="${siteMaintenance.rebuildSearchIndex }" title="${i18n().rebuild_search_index}">${i18n().rebuild_search_index}</a></li>
</#if>
<#if siteMaintenance.rebuildVisCache?has_content>
<li role="listitem"><a href="${siteMaintenance.rebuildVisCache}" title="${i18n().rebuild_vis_cache}">${i18n().rebuild_vis_cache}</a></li>
</#if>
<#if siteMaintenance.recomputeInferences?has_content>
<li role="listitem"><a href="${siteMaintenance.recomputeInferences}" title="${i18n().recompute_inferences}">${i18n().recompute_inferences_mixed_caps}</a></li>
</#if>
<#if siteMaintenance.restrictLogins?has_content>
<li role="listitem"><a href="${siteMaintenance.restrictLogins}" title="${i18n().restrict_logins}">${i18n().restrict_logins_mixed_caps}</a></li>
</#if>
<#if siteMaintenance.activateDeveloperPanel?has_content>
<li role="listitem"><a href="${siteMaintenance.activateDeveloperPanel}" title="${i18n().activate_developer_panel}">${i18n().activate_developer_panel_mixed_caps}</a></li>
</#if>
</ul>
</section>
</#if>

View file

@ -9,7 +9,7 @@
-->
<#assign rangeOptionsExist = true />
<#assign rangeVClassURI = editConfiguration.objectPredicateProperty.rangeVClassURI!"" />
<#assign objectTypes = editConfiguration.pageData.objectTypes />
<#assign objectTypesSize = objectTypes?length />
<#assign objectTypesExist = false />
@ -40,7 +40,7 @@
<#assign formTitle = editConfiguration.formTitle />
<#if editConfiguration.formTitle?contains("collaborator") >
<#assign formTitle = "${i18n().select_existing_collaborator(editConfiguration.subjectName)}" />
<#elseif editConfiguration.objectPredicateProperty.rangeVClassURI?contains("IAO_0000030")>
<#elseif rangeVClassURI?contains("IAO_0000030")>
<#assign formTitle = "${i18n().select_an_existing_document}" + " ${i18n().for} " + editConfiguration.subjectName/>
</#if>
<#--In order to fill out the subject-->

View file

@ -30,9 +30,9 @@
<input type="hidden" name="subjectUri" value="${editConfiguration.subjectUri}" role="input" />
<input type="hidden" name="predicateUri" value="${editConfiguration.predicateUri}" role="input" />
<input type="hidden" name="domainUri" value="${editConfiguration.domainUri}" role="input" />
<input type="hidden" name="rangeUri" value="${editConfiguration.rangeUri}" role="input" />
<input type="hidden" name="domainUri" value="${editConfiguration.domainUri!}" role="input" />
<input type="hidden" name="rangeUri" value="${editConfiguration.rangeUri!}" role="input" />
<input type="hidden" name="deleteObjectUri" value="${editConfiguration.customDeleteObjectUri!}" />
<#if editConfiguration.dataProperty = true>
<input type="hidden" name="datapropKey" value="${editConfiguration.datapropKey}" role="input" />
<input type="hidden" name="vitroNsProp" value="${editConfiguration.vitroNsProperty}" role="input" />

View file

@ -7,8 +7,8 @@
<input type="hidden" name="subjectUri" value="${editConfiguration.subjectUri}"/>
<input type="hidden" name="predicateUri" value="${editConfiguration.predicateUri}"/>
<input type="hidden" name="domainUri" value="${editConfiguration.domainUri}"/>
<input type="hidden" name="rangeUri" value="${editConfiguration.rangeUri}"/>
<input type="hidden" name="domainUri" value="${editConfiguration.domainUri!}"/>
<input type="hidden" name="rangeUri" value="${editConfiguration.rangeUri!}"/>
<input type="hidden" name="cmd" value="delete"/>
<input type="hidden" name="editKey" value="${editConfiguration.editKey}"/>
<#if editConfiguration.dataProperty = true>

View file

@ -17,6 +17,8 @@
<input type="hidden" value="${editConfiguration.subjectUri}" name="subjectUri" role="input" />
<input type="hidden" value="${editConfiguration.predicateUri}" name="predicateUri" role="input" />
<input type="hidden" value="${objectUri}" name="objectUri" role="input" />
<input type="hidden" name="domainUri" value="${editConfiguration.domainUri!}"/>
<input type="hidden" name="rangeUri" value="${editConfiguration.rangeUri!}"/>
<input type="hidden" value="create" name="cmd" role="input" />
<select id="typeOfNew" name="typeOfNew" role="selection">

View file

@ -6,8 +6,9 @@
<#if (rangeOptions?keys?size > 0)>
<#assign rangeOptionsExist = true/>
</#if>
<#assign rangeUri = editConfiguration.objectPredicateProperty.rangeVClassURI!"" />
<#assign formTitle = editConfiguration.formTitle />
<#if editConfiguration.objectPredicateProperty.rangeVClassURI?contains("IAO_0000030")>
<#if rangeUri?contains("IAO_0000030")>
<#assign formTitle = "${i18n().select_an_existing_document}" + " ${i18n().for} " + editConfiguration.subjectName/>
</#if>
<h2>${formTitle}</h2>

View file

@ -118,7 +118,7 @@ name will be used as the label. -->
</#if>
-->
<#if editable>
<h2 id="${property.localName}">${label} ${addLink!} ${verboseDisplay!}</h2>
<h2 id="${property.localName!}">${label} ${addLink!} ${verboseDisplay!}</h2>
</#if>
</#macro>
@ -134,15 +134,15 @@ name will be used as the label. -->
<#local domainUri = "" />
</#if>
<#if editable>
<#if property.addUrl?has_content>
<#local url = property.addUrl>
<#if url?has_content>
<@showAddLink property.localName label url rangeUri domainUri/>
</#if>
</#if>
</#macro>
<#macro showAddLink propertyLocalName label url rangeUri domainUri="">
<#if (rangeUri?contains("Authorship") && domainUri?contains("IAO_0000030")) || (rangeUri?contains("Editorship") && domainUri?contains("IAO_0000030"))|| rangeUri?contains("URL") || label == "hasResearchArea">
<#if (rangeUri?contains("Authorship") && domainUri?contains("IAO_0000030")) || (rangeUri?contains("Editorship") && domainUri?contains("IAO_0000030"))|| rangeUri?contains("URL") || propertyLocalName == "hasResearchArea">
<a class="add-${propertyLocalName}" href="${url}" title="${i18n().manage_list_of} ${label?lower_case}">
<img class="add-individual" src="${urls.images}/individual/manage-icon.png" alt="${i18n().manage}" /></a>
<#else>
@ -171,22 +171,22 @@ name will be used as the label. -->
<#macro editingLinks propertyLocalName propertyName statement editable rangeUri="">
<#if editable >
<#if (!rangeUri?contains("Authorship") && !rangeUri?contains("URL") && !rangeUri?contains("Editorship") && propertyLocalName != "hasResearchArea")>
<@editLink propertyLocalName propertyName statement />
<@deleteLink propertyLocalName propertyName statement />
<@editLink propertyLocalName propertyName statement rangeUri/>
<@deleteLink propertyLocalName propertyName statement rangeUri/>
</#if>
</#if>
</#macro>
<#macro editLink propertyLocalName propertyName statement>
<#macro editLink propertyLocalName propertyName statement rangeUri="">
<#if propertyLocalName?contains("ARG_2000028")>
<#if propertyName?contains("mailing address")>
<#if rangeUri?contains("Address")>
<#local url = statement.editUrl + "&addressUri=" + "${statement.address!}">
<#elseif propertyName?contains("phone") || propertyName?contains("fax")>
<#elseif rangeUri?contains("Telephone") || rangeUri?contains("Fax")>
<#local url = statement.editUrl + "&phoneUri=" + "${statement.phone!}">
<#elseif propertyName?contains("primary email") || propertyName?contains("additional emails")>
<#elseif rangeUri?contains("Work") || rangeUri?contains("Email")>
<#local url = statement.editUrl + "&emailUri=" + "${statement.email!}">
<#elseif propertyName?contains("full name")>
<#elseif rangeUri?contains("Name")>
<#local url = statement.editUrl + "&fullNameUri=" + "${statement.fullName!}">
<#elseif propertyName?contains("preferred title")>
<#elseif rangeUri?contains("Title")>
<#local url = statement.editUrl + "&titleUri=" + "${statement.title!}">
</#if>
<#else>
@ -202,9 +202,23 @@ name will be used as the label. -->
<a class="edit-${propertyLocalName}" href="${url}" title="${i18n().edit_entry}"><img class="edit-individual" src="${urls.images}/individual/editIcon.gif" alt="${i18n().edit_entry}" /></a>
</#macro>
<#macro deleteLink propertyLocalName propertyName statement>
<#macro deleteLink propertyLocalName propertyName statement rangeUri="">
<#local url = statement.deleteUrl>
<#if url?has_content>
<#--We need to specify the actual object to be deleted as it is different from the object uri-->
<#if propertyLocalName?contains("ARG_2000028")>
<#if rangeUri?contains("Address")>
<#local url = url + "&deleteObjectUri=" + "${statement.address!}">
<#elseif rangeUri?contains("Telephone") || rangeUri?contains("Fax")>
<#local url = url + "&deleteObjectUri=" + "${statement.phone!}">
<#elseif rangeUri?contains("Work") || rangeUri?contains("Email")>
<#local url = url + "&deleteObjectUri=" + "${statement.email!}">
<#elseif rangeUri?contains("Name")>
<#local url = url + "&deleteObjectUri=" + "${statement.fullName!}">
<#elseif rangeUri?contains("Title")>
<#local url = url + "&deleteObjectUri=" + "${statement.title!}">
</#if>
</#if>
<@showDeleteLink propertyLocalName url />
</#if>
</#macro>
@ -217,6 +231,9 @@ name will be used as the label. -->
<#local verboseDisplay = property.verboseDisplay!>
<#if verboseDisplay?has_content>
<section class="verbosePropertyListing">
<#if verboseDisplay.fauxProperty??>
a faux property of
</#if>
<a class="propertyLink" href="${verboseDisplay.propertyEditUrl}" title="${i18n().name}">${verboseDisplay.localName}</a>
(<span>${property.type?lower_case}</span> property);
order in group: <span>${verboseDisplay.displayRank};</span>
@ -260,7 +277,8 @@ name will be used as the label. -->
<#assign labelPropertyUri = ("http://www.w3.org/2000/01/rdf-schema#label"?url) />
<#assign useEditLink = false />
<#--edit link used if in edit mode and only one label and one language-->
<#if labelCount = 1 && editable && localesCount = 1 >
<#--locales count may be 0 in case where no languages/selectable locales are specified-->
<#if labelCount = 1 && editable && (localesCount >= 0) >
<#assign useEditLink = true/>
</#if>
<#local label = individual.nameStatement>

View file

@ -0,0 +1,6 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<div id="developerPanel" > </div>
<script>developerAjaxUrl = '${urls.developerAjax}'</script>
${scripts.add('<script type="text/javascript" src="${urls.base}/js/developer/developerPanel.js"></script>')}
<link rel="stylesheet" href="${urls.base}/css/developer/developerPanel.css" />

View file

@ -0,0 +1,105 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#macro showCheckbox key>
<input type="checkbox" id="${key}" <#if settings[key]>checked</#if>>
</#macro>
<#macro showTextbox key>
<input type="text" id="${key}" size="30" value="${settings[key]}" >
</#macro>
<#if !settings.developerEnabled>
<#elseif !settings.mayControl>
<div class="developer">
<h1>${siteName} is running in developer mode.</h1>
</div>
<#else>
<div class="developer">
<h1 id="developerPanelClickMe">${siteName} is running in developer mode.
<span id="developerPanelClickText">(click for Options)</span>
</h1>
<div id="developerPanelBody">
<div>
<label>
<@showCheckbox "developerEnabled" />
Enable developer mode
</label>
</div>
<div class="devright">
<div class="container">
Page configuration
<label>
<@showCheckbox "developerPageContentsLogCustomListView" />
Log the use of custom list view XML files.
</label>
<label>
<@showCheckbox "developerPageContentsLogCustomShortView" />
Log the use of custom short views in search, index and browse pages.
</label>
</div>
<div class="container">
Language support
<label>
<@showCheckbox "developerI18nDefeatCache" />
Defeat the cache of language property files
</label>
<label>
<@showCheckbox "developerI18nLogStringRequests" />
Log the retrieval of language strings
</label>
</div>
<div class="container">
Links
<br/>
<a href="${urls.base}/admin/log4j.jsp">Set log levels</a>
<a href="${urls.base}/admin/showAuth">Show authorization info</a>
<a href="${urls.base}/admin/showThreads">Show background threads</a>
</div>
</div>
<div class="devleft">
<div class="container">
Freemarker templates
<label>
<@showCheckbox "developerDefeatFreemarkerCache" />
Defeat the template cache
</label>
<label>
<@showCheckbox "developerInsertFreemarkerDelimiters" />
Insert HTML comments at start and end of templates
</label>
</div>
<div class="container">
SPARQL Queries
<label>
<@showCheckbox "developerLoggingRDFServiceEnable" />
Log each query
</label>
<div class="within">
<label>
<@showCheckbox "developerLoggingRDFServiceStackTrace" />
Add stack trace
</label>
<label>
Restrict by query string
<@showTextbox "developerLoggingRDFServiceQueryRestriction" />
</label>
<label>
Restrict by calling stack
<@showTextbox "developerLoggingRDFServiceStackRestriction" />
</label>
</div>
</div>
</div>
<div>
<input type="button" id="developerPanelSaveButton" value="Save Settings" name="foo" />
</div>
</div>
</div>
</#if>

View file

@ -10,7 +10,7 @@
<#if selectLocale??>
<#list selectLocale.locales as locale>
<li>
<a href="${selectLocale.selectLocaleUrl}?selection=${locale.code}" title="${i18n().select_locale} -- ${locale.label}">
<a href="${selectLocale.selectLocaleUrl}?selection=${locale.code}" title="${i18n().select_locale} -- ${locale.label}" <#if locale.selected>style="padding-bottom:1px;border-bottom: 1px solid #ccdfe6"</#if>>
<img src="${locale.imageUrl}" height="15" style="vertical-align:middle" alt="${locale.label}"/>
</a>
</li>

View file

@ -1,5 +1,7 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#include "developer.ftl">
<nav role="navigation">
<ul id="main-nav" role="list">
<#list menu.items as item>