From 1f5c34bde96c890537280e27bac70aebb781680a Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 20 Nov 2013 11:02:12 -0500 Subject: [PATCH 01/18] VIVO-533 Simplify the code in the template loader. --- .../loader/FreemarkerTemplateLoader.java | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java index 33adcb152..2ae556862 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java @@ -15,6 +15,8 @@ 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; @@ -178,64 +180,71 @@ public class FreemarkerTemplateLoader implements TemplateLoader { * 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 searchTerm) { - this(Paths.get(searchTerm)); + public PathPieces(String pathString) { + this(Paths.get(pathString)); } public PathPieces(Path path) { this.path = path; String filename = path.getFileName().toString(); - int dotHere = filename.lastIndexOf('.'); - String basename; - if (dotHere != -1) { - basename = filename.substring(0, dotHere); - this.extension = filename.substring(dotHere); - } else { - basename = filename; - this.extension = ""; - } - int break2 = basename.lastIndexOf('_'); - int break1 = basename.lastIndexOf('_', break2 - 1); - if (break1 != -1) { - this.base = basename.substring(0, break1); - this.language = basename.substring(break1, break2); - this.region = basename.substring(break2); - } else if (break2 != -1) { - this.base = basename.substring(0, break2); - this.language = basename.substring(break2); - this.region = ""; + 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 { - this.base = basename; - this.language = ""; - this.region = ""; + base = filename; + language = ""; + region = ""; + extension = ""; } } - /** This is the search term. Does that candidate qualify as a result? */ + 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; // match language and region + return 3; // exact match. } else { - return 2; // match language, default region. + return 2; // same language, approximate region. } } else { - return 1; // default language. + return 1; // approximate language. } } else { return -1; // doesn't match. From 00f3e363e5b28a15b285f04c52151712598b643f Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 20 Nov 2013 15:50:51 -0500 Subject: [PATCH 02/18] VIVO-541 Add functions and formatting to the developer panel. --- webapp/config/example.developer.properties | 40 ++++++- .../impl/logging/RDFServiceLogger.java | 78 +++++++++---- .../FakeApplicationOntologyService.java | 6 + .../services/shortview/ShortViewLogger.java | 47 ++++++++ .../shortview/ShortViewServiceImpl.java | 4 +- .../utils/developer/DeveloperSettings.java | 24 +++- .../customlistview/CustomListViewLogger.java | 38 +++++++ .../customlistview/PropertyListConfig.java | 2 + webapp/web/js/developer/developerPanel.js | 10 +- .../page/partials/developerPanel.ftl | 103 +++++++++++++----- 10 files changed, 288 insertions(+), 64 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java diff --git a/webapp/config/example.developer.properties b/webapp/config/example.developer.properties index 482cecd21..78a1d2a72 100644 --- a/webapp/config/example.developer.properties +++ b/webapp/config/example.developer.properties @@ -32,6 +32,7 @@ # # developer.permitAnonymousControl + #------------------------------------------------------------------------------ # Freemarker #------------------------------------------------------------------------------ @@ -52,6 +53,25 @@ # # 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 #------------------------------------------------------------------------------ @@ -98,10 +118,18 @@ # developer.loggingRDFService.stackTrace = true # -# If SPARQL query logging is enabled, a regular expression can be used to -# restrict the number of entries that are produced. The expression is -# tested against each line in the (unabridged) stack trace. If the -# expression doesn't match any line in the stack trace, then no log entry -# is made. The default is 'false'. +# 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.restriction = true +# 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 = .* diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java index d7f0ffa7a..c0950e967 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java @@ -23,9 +23,12 @@ import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; * * 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. @@ -47,7 +50,8 @@ public class RDFServiceLogger implements AutoCloseable { private boolean isEnabled; private boolean traceRequested; - private Pattern restriction; + private Pattern queryStringRestriction; + private Pattern callStackRestriction; private String methodName; private List trace = Collections.emptyList(); @@ -62,7 +66,7 @@ public class RDFServiceLogger implements AutoCloseable { if (isEnabled && log.isInfoEnabled()) { loadStackTrace(); - if (passesRestrictions()) { + if (passesQueryRestriction() && passesStackRestriction()) { this.startTime = System.currentTimeMillis(); } } @@ -72,20 +76,23 @@ public class RDFServiceLogger implements AutoCloseable { 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); + } - String restrictionString = settings - .getString(Keys.LOGGING_RDF_RESTRICTION); - if (StringUtils.isBlank(restrictionString)) { - restriction = null; - } else { - try { - restriction = Pattern.compile(restrictionString); - } catch (Exception e) { - log.error("Failed to compile the pattern for " - + Keys.LOGGING_RDF_RESTRICTION + " = " + restriction - + " " + e); - isEnabled = false; - } + 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; } - for (StackTraceElement ste : trace) { - if (restriction.matcher(ste.getClassName()).find()) { - 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 false; + 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) { + stack.append(ste.getClassName()).append(" ") + .append(ste.getMethodName()).append(" "); + } + return stack.deleteCharAt(stack.length() - 1).toString(); } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java index 92fcc8ae7..a650620cc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java @@ -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. */ diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java new file mode 100644 index 000000000..220185445 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java @@ -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); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java index 8e18208a0..c072f866e 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java @@ -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()); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java index 822e434e4..169491372 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java @@ -82,9 +82,29 @@ public class DeveloperSettings { * Don't log with the LoggingRDFService unless the calling stack meets * this restriction. */ - LOGGING_RDF_RESTRICTION("developer.loggingRDFService.restriction", - false); + 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; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java new file mode 100644 index 000000000..2509af61c --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java @@ -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); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java index 651114aff..ad645f393 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java @@ -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()); diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index 8c0c9a2b1..f7d881709 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -42,13 +42,16 @@ function DeveloperPanel(developerAjaxUrl) { 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("developerLoggingRDFServiceRestriction").disabled = !rdfServiceEnabled; + document.getElementById("developerLoggingRDFServiceQueryRestriction").disabled = !rdfServiceEnabled; + document.getElementById("developerLoggingRDFServiceStackRestriction").disabled = !rdfServiceEnabled; } function collectFormData() { @@ -56,11 +59,14 @@ function DeveloperPanel(developerAjaxUrl) { 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("developerLoggingRDFServiceRestriction", data); + getText("developerLoggingRDFServiceQueryRestriction", data); + getText("developerLoggingRDFServiceStackRestriction", data); return data; } diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index 533de3d2c..a8b429f26 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -6,7 +6,7 @@ <#macro showTextbox key> - + @@ -14,17 +14,40 @@ div.developer { background-color: #f7dd8a; padding: 0px 10px 0px 10px; - font-size: small; font-variant: small-caps; } div.developer #developerPanelBody { display: none; + line-height: 1em; + font-size: small; } -div.developer .container { - border: thin groove black +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 0px 10px; + margin: 3px 0px 3px 0px; +} + +div.developer div.within { + padding-left: 1em; +} + +div.developer input[type="text"] { + padding: 2px 10px 2px 10px; + line-height: 1em; + margin: 2px 2px 2px 2px; + } + <#if !settings.developerEnabled> @@ -38,40 +61,26 @@ div.developer .container { (click for Options)
-
+
- +
+ +
- Freemarker templates + Page configuration
-
- SPARQL Queries - - - -
-
Language support
+
+ +
+
+ Freemarker templates + + +
+
+ SPARQL Queries + +
+ + + +
+
+
+ +
- +
\ No newline at end of file From 99da2bc5a436e54799922ea9bfef147de2809902 Mon Sep 17 00:00:00 2001 From: brianjlowe Date: Wed, 20 Nov 2013 16:48:24 -0500 Subject: [PATCH 03/18] VIVO-561 faux property indication in verbose display --- .../individual/PropertyTemplateModel.java | 24 +++++++++++++++++++ .../freemarker/lib/lib-properties.ftl | 6 +++++ 2 files changed, 30 insertions(+) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyTemplateModel.java index f7d5c603b..2fe95a71c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyTemplateModel.java @@ -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); diff --git a/webapp/web/templates/freemarker/lib/lib-properties.ftl b/webapp/web/templates/freemarker/lib/lib-properties.ftl index 95b467eda..65b2e5eb8 100644 --- a/webapp/web/templates/freemarker/lib/lib-properties.ftl +++ b/webapp/web/templates/freemarker/lib/lib-properties.ftl @@ -231,7 +231,13 @@ name will be used as the label. --> <#local verboseDisplay = property.verboseDisplay!> <#if verboseDisplay?has_content>
+ <#if verboseDisplay.fauxProperty??> + (faux property of + ${verboseDisplay.localName} + <#if verboseDisplay.fauxProperty??> + ) + (${property.type?lower_case} property); order in group: ${verboseDisplay.displayRank}; display level: ${verboseDisplay.displayLevel}; From 3bca31ea14889568adcb0041fcd324294725b906 Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 20 Nov 2013 17:06:48 -0500 Subject: [PATCH 04/18] VIVO-541 Rearrange the links on the site admin page. --- .../languages/es_GO/i18n/all_es_GO.properties | 2 +- .../freemarker/BaseSiteAdminController.java | 12 +++---- webapp/web/i18n/all.properties | 2 +- .../siteAdmin/siteAdmin-indexCacheRebuild.ftl | 27 ---------------- .../body/siteAdmin/siteAdmin-main.ftl | 2 +- .../siteAdmin/siteAdmin-siteConfiguration.ftl | 4 --- .../siteAdmin/siteAdmin-siteMaintenance.ftl | 31 +++++++++++++++++++ 7 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-indexCacheRebuild.ftl create mode 100644 webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteMaintenance.ftl diff --git a/webapp/languages/es_GO/i18n/all_es_GO.properties b/webapp/languages/es_GO/i18n/all_es_GO.properties index 85e25c9cf..5544aafb0 100644 --- a/webapp/languages/es_GO/i18n/all_es_GO.properties +++ b/webapp/languages/es_GO/i18n/all_es_GO.properties @@ -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 diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java index fd988add8..1a10c92a3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java @@ -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 getIndexCacheRebuildUrls(VitroRequest vreq) { + protected Map getSiteMaintenanceUrls(VitroRequest vreq) { Map urls = new HashMap(); @@ -73,6 +73,10 @@ 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});"); } @@ -147,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; } diff --git a/webapp/web/i18n/all.properties b/webapp/web/i18n/all.properties index 336d1fefc..d201b66c9 100644 --- a/webapp/web/i18n/all.properties +++ b/webapp/web/i18n/all.properties @@ -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 diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-indexCacheRebuild.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-indexCacheRebuild.ftl deleted file mode 100644 index c2317ce27..000000000 --- a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-indexCacheRebuild.ftl +++ /dev/null @@ -1,27 +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> -
-

${i18n().refresh_content}

- - -
- \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl index ef8bb2242..e957d435f 100644 --- a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl +++ b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl @@ -13,5 +13,5 @@ ${stylesheets.add('') <#include "siteAdmin-siteConfiguration.ftl"> <#include "siteAdmin-ontologyEditor.ftl"> <#include "siteAdmin-advancedDataTools.ftl"> - <#include "siteAdmin-indexCacheRebuild.ftl"> + <#include "siteAdmin-siteMaintenance.ftl"> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteConfiguration.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteConfiguration.ftl index eb9a38c76..8ea8df055 100644 --- a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteConfiguration.ftl +++ b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteConfiguration.ftl @@ -23,10 +23,6 @@
  • ${i18n().menu_ordering_mixed_caps}
  • - <#if siteConfig.restrictLogins?has_content> -
  • ${i18n().restrict_logins_mixed_caps}
  • - - <#if siteConfig.siteInfo?has_content>
  • ${i18n().site_information}
  • diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteMaintenance.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteMaintenance.ftl new file mode 100644 index 000000000..ab1a20189 --- /dev/null +++ b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-siteMaintenance.ftl @@ -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> +
    +

    ${i18n().site_maintenance}

    + + +
    + \ No newline at end of file From 7804040e97ac57966fa1ee25dbcd86b53dbf08d3 Mon Sep 17 00:00:00 2001 From: tworrall Date: Thu, 21 Nov 2013 10:31:22 -0500 Subject: [PATCH 05/18] VIVO-561 --- webapp/web/templates/freemarker/lib/lib-properties.ftl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/webapp/web/templates/freemarker/lib/lib-properties.ftl b/webapp/web/templates/freemarker/lib/lib-properties.ftl index 65b2e5eb8..575175937 100644 --- a/webapp/web/templates/freemarker/lib/lib-properties.ftl +++ b/webapp/web/templates/freemarker/lib/lib-properties.ftl @@ -232,12 +232,9 @@ name will be used as the label. --> <#if verboseDisplay?has_content>
    <#if verboseDisplay.fauxProperty??> - (faux property of + a faux property of ${verboseDisplay.localName} - <#if verboseDisplay.fauxProperty??> - ) - (${property.type?lower_case} property); order in group: ${verboseDisplay.displayRank}; display level: ${verboseDisplay.displayLevel}; From 11d766ba344ace83403763652df6998881f847fc Mon Sep 17 00:00:00 2001 From: brianjlowe Date: Thu, 21 Nov 2013 16:14:26 -0500 Subject: [PATCH 06/18] VIVO-564 reimplemented RDFServiceSparql.sparqlSelectQuery() using direct HttpClient calls instead of using Jena's stuff --- .../impl/sparql/RDFServiceSparql.java | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java index b207f1681..b20c6cba3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java @@ -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() { @@ -278,13 +282,28 @@ 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(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + 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) { case CSV: @@ -301,12 +320,14 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService { break; 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 { From 9efd0d45aa049fa2a910d0e562246946bc676489 Mon Sep 17 00:00:00 2001 From: tworrall Date: Fri, 22 Nov 2013 12:49:15 -0500 Subject: [PATCH 07/18] hasResearchAreas was not displaying the manage icon --- webapp/web/templates/freemarker/lib/lib-properties.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/web/templates/freemarker/lib/lib-properties.ftl b/webapp/web/templates/freemarker/lib/lib-properties.ftl index 575175937..4a786d459 100644 --- a/webapp/web/templates/freemarker/lib/lib-properties.ftl +++ b/webapp/web/templates/freemarker/lib/lib-properties.ftl @@ -142,7 +142,7 @@ name will be used as the label. --> <#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"> ${i18n().manage} <#else> From 04f9d9839919714075c66c702d1a2b3d6b278ef5 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 22 Nov 2013 13:46:32 -0500 Subject: [PATCH 08/18] VIVO-568 Exclude individuals whose most specific type is a Vitro term It looks like this was the intent of ExcludeNonFlagVitro, but it was getting the VClasses of the individual, which filtered out things like PropertyGroup, ClassGroup, etc. Changed it to test the most specific types instead, which seems to be right. --- .../documentBuilding/ExcludeNonFlagVitro.java | 71 +++++++++++-------- .../IndividualToSolrDocument.java | 3 + 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ExcludeNonFlagVitro.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ExcludeNonFlagVitro.java index 3381423d0..fdd2df365 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ExcludeNonFlagVitro.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ExcludeNonFlagVitro.java @@ -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; - } + @Override + public String checkForExclusion(Individual ind) { + if (ind == null) { + return DONT_EXCLUDE; + } + + List mostSpecificTypeUris = ind.getMostSpecificTypeURIs(); + if (mostSpecificTypeUris == null) { + return DONT_EXCLUDE; + } + + 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 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 DONT_EXCLUDE; + } - String skipIfVitro(Individual ind, List vclasses) { - for( VClass type: vclasses ){ - if( type != null && type.getURI() != null ){ - String typeURI = type.getURI(); - - if(typeURI.startsWith( VitroVocabulary.vitroURI ) - && ! typeURI.startsWith(VitroVocabulary.vitroURI + "Flag") ){ - - return "Skipped " + ind.getURI()+" because in " - + VitroVocabulary.vitroURI + " namespace"; - } - } - } - return null; - } - } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/IndividualToSolrDocument.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/IndividualToSolrDocument.java index c32bb2503..a2f62782c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/IndividualToSolrDocument.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/IndividualToSolrDocument.java @@ -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) { From 98f231a8637189fcf7e83bb2227210caf6cd5817 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 22 Nov 2013 15:44:28 -0500 Subject: [PATCH 09/18] VIVO-541 Add links to the developer panel. --- webapp/web/css/developer/developerPanel.css | 48 ++++++++++++++++++ .../freemarker/page/partials/developer.ftl | 1 + .../page/partials/developerPanel.ftl | 49 +++---------------- 3 files changed, 57 insertions(+), 41 deletions(-) create mode 100644 webapp/web/css/developer/developerPanel.css diff --git a/webapp/web/css/developer/developerPanel.css b/webapp/web/css/developer/developerPanel.css new file mode 100644 index 000000000..ae61e5401 --- /dev/null +++ b/webapp/web/css/developer/developerPanel.css @@ -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; +} diff --git a/webapp/web/templates/freemarker/page/partials/developer.ftl b/webapp/web/templates/freemarker/page/partials/developer.ftl index aad2772f6..115718197 100644 --- a/webapp/web/templates/freemarker/page/partials/developer.ftl +++ b/webapp/web/templates/freemarker/page/partials/developer.ftl @@ -3,3 +3,4 @@
    ${scripts.add('')} + \ No newline at end of file diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index a8b429f26..82b2a0c80 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -9,47 +9,6 @@ - - - <#if !settings.developerEnabled> <#elseif !settings.mayControl>
    @@ -92,6 +51,14 @@ div.developer input[type="text"] { Log the retrieval of language strings
    + +
    From 2df39c9e24d99c66c2975479bd7ddb6beb8aa614 Mon Sep 17 00:00:00 2001 From: brianjlowe Date: Mon, 25 Nov 2013 10:42:32 -0500 Subject: [PATCH 10/18] change to PropertyGroupTemplateModel to prevent drawing meaningless headers when a DataProperty is not visible or editable --- .../individual/PropertyGroupTemplateModel.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyGroupTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyGroupTemplateModel.java index b50469188..91d463f54 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyGroupTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/PropertyGroupTemplateModel.java @@ -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"); } } } From 757d643f2d2c4d562e7f9161a81171c2ede4a4ac Mon Sep 17 00:00:00 2001 From: tworrall Date: Mon, 25 Nov 2013 10:51:25 -0500 Subject: [PATCH 11/18] fix cross browser console issue --- webapp/web/js/vitroUtils.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/webapp/web/js/vitroUtils.js b/webapp/web/js/vitroUtils.js index e910efa74..10e5f564b 100644 --- a/webapp/web/js/vitroUtils.js +++ b/webapp/web/js/vitroUtils.js @@ -13,20 +13,16 @@ $(document).ready(function(){ jQuery('section#flash-message').css('display', 'none').fadeIn(1500); ///////////////////////////// - // Home search fiter + // Home search filter // Toggle filter select list var $searchFilterList = $('#filter-search-nav'); var $isFilterOpen = false; - console.log("Filter is open = " + $isFilterOpen); - $('a.filter-search').click(function(e) { e.preventDefault(); if (!$isFilterOpen) { - console.log("Filter is closed = " + $isFilterOpen); - //Change button filter state to selected //$(this).css('background','url(../../themes/vivo-cornell/images/filteredSearchActive.gif) no-repeat right top'); $(this).removeClass('filter-default'); @@ -37,7 +33,6 @@ $(document).ready(function(){ $isFilterOpen = true; - console.log("open"); } else { //Change button filter state to default //$('a.filter-search').css('background','url(../../themes/vivo-cornell/images/filteredSearch.gif) no-repeat right top'); @@ -49,7 +44,6 @@ $(document).ready(function(){ $isFilterOpen = false; - console.log("closed"); } }); @@ -63,7 +57,6 @@ $(document).ready(function(){ //Selected filter feedback $('.search-filter-selected').text(''); $('input[name="classgroup"]').val(''); - console.log("ALL"); } else { $('.search-filter-selected').text($(this).text()).fadeIn('slow'); @@ -110,6 +103,5 @@ $(document).ready(function(){ } - console.log("HIDE input value ") ; }); }); From 0b43a4409715cd0f7a549ae69c8835c3302b35b2 Mon Sep 17 00:00:00 2001 From: j2blake Date: Mon, 25 Nov 2013 11:48:11 -0500 Subject: [PATCH 12/18] VIVO-522 fix memory leak in FreemarkerConfigurationImpl --- .../config/FreemarkerConfigurationImpl.java | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java index 617a09f6c..7d2c05c9d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java @@ -3,6 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.freemarker.config; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -35,28 +36,52 @@ import freemarker.template.utility.DeepUnwrap; * Extend the Freemarker Configuration class to include some information that is * particular to the current request. * - * Takes advantage of the fact that each servlet request runs in a separate - * thread. Stores the request-based information in a ThreadLocal. Override any - * methods that should return that information instead of (or in addition to) - * the common info. + * A reference to the current request is not always available to the Freemarker + * Configuration, so we take advantage of the fact that each request runs in a + * separate thread, and store a reference to that request in a ThreadLocal + * object. + * + * Then, we override any methods that should return that request-based + * information instead of (or in addition to) the common info. * * Only the getters are overridden, not the setters. So if you call * setAllSharedVariables(), for example, it will have no effect on the * request-based information. + * + * Notice that the reference to the current request is actually stored through a + * WeakReference. This is because the ThreadLocal will not be cleared when the + * webapp is stopped, so none of the references from that ThreadLocal are + * eligible for garbage collection. If any of those references is an instance of + * a class that is loaded by the webapp, then the webapp ClassLoader is not + * eligible for garbage collection. This would be a huge memory leak. + * + * Thanks to the WeakReference, the request is eligible for garbage collection + * if nothing else refers to it. In theory, this means that the WeakReference + * could return a null, but if the garbage collector has taken the request, then + * who is invoking this object? */ public class FreemarkerConfigurationImpl extends Configuration { private static final Log log = LogFactory .getLog(FreemarkerConfigurationImpl.class); - private final ThreadLocal rbiRef = new ThreadLocal<>(); + private static final String ATTRIBUTE_NAME = RequestBasedInformation.class + .getName(); + + private final ThreadLocal> reqRef = new ThreadLocal<>(); void setRequestInfo(HttpServletRequest req) { - rbiRef.set(new RequestBasedInformation(req, this)); + reqRef.set(new WeakReference<>(req)); + req.setAttribute(ATTRIBUTE_NAME, new RequestBasedInformation(req, this)); + } + + private RequestBasedInformation getRequestInfo() { + HttpServletRequest req = reqRef.get().get(); + return (RequestBasedInformation) req.getAttribute(ATTRIBUTE_NAME); } @Override public Object getCustomAttribute(String name) { - Map attribs = rbiRef.get().getCustomAttributes(); + Map attribs = getRequestInfo().getCustomAttributes(); if (attribs.containsKey(name)) { return attribs.get(name); } else { @@ -66,13 +91,13 @@ public class FreemarkerConfigurationImpl extends Configuration { @Override public String[] getCustomAttributeNames() { - Set rbiNames = rbiRef.get().getCustomAttributes().keySet(); + Set rbiNames = getRequestInfo().getCustomAttributes().keySet(); return joinNames(rbiNames, super.getCustomAttributeNames()); } @Override public TemplateModel getSharedVariable(String name) { - Map vars = rbiRef.get().getSharedVariables(); + Map vars = getRequestInfo().getSharedVariables(); if (vars.containsKey(name)) { return vars.get(name); } else { @@ -82,7 +107,7 @@ public class FreemarkerConfigurationImpl extends Configuration { @Override public Set getSharedVariableNames() { - Set rbiNames = rbiRef.get().getSharedVariables().keySet(); + Set rbiNames = getRequestInfo().getSharedVariables().keySet(); @SuppressWarnings("unchecked") Set superNames = super.getSharedVariableNames(); @@ -94,7 +119,7 @@ public class FreemarkerConfigurationImpl extends Configuration { @Override public Locale getLocale() { - return rbiRef.get().getReq().getLocale(); + return getRequestInfo().getReq().getLocale(); } private String[] joinNames(Set nameSet, String[] nameArray) { From 70298026ea242c2190198f6927235a1a08233468 Mon Sep 17 00:00:00 2001 From: j2blake Date: Mon, 25 Nov 2013 12:31:51 -0500 Subject: [PATCH 13/18] Reduce warning messages in unit tests. We get a warning on any attempt to use anything that may be affected by DeveloperSettings, because DeveloperSetting tries to use ConfigurationProperties to find its properties file. We could avoid this by requiring the use of a ConfigurationPropertiesStub, but it seems to make more sense to routinely create a DeveloperSettingsStub on each ServletContextStub. --- .../utils/developer/DeveloperSettings.java | 4 +-- .../developer/DeveloperSettingsStub.java | 33 +++++++++++++++++++ .../javax/servlet/ServletContextStub.java | 7 ++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java index 169491372..f883281f9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java @@ -179,7 +179,7 @@ public class DeveloperSettings { // The factory // ---------------------------------------------------------------------- - private static final String ATTRIBUTE_NAME = DeveloperSettings.class + protected static final String ATTRIBUTE_NAME = DeveloperSettings.class .getName(); public static DeveloperSettings getBean(HttpServletRequest req) { @@ -203,7 +203,7 @@ public class DeveloperSettings { private final Map settings = new EnumMap<>(Keys.class); - private DeveloperSettings(ServletContext ctx) { + protected DeveloperSettings(ServletContext ctx) { updateFromFile(ctx); } diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java new file mode 100644 index 000000000..6e2871596 --- /dev/null +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java @@ -0,0 +1,33 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package stubs.edu.cornell.mannlib.vitro.webapp.utils.developer; + +import javax.servlet.ServletContext; + +import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; + +/** + * Do everything that a standard DeveloperSettings would do, except loading from + * a properties file. + * + * That way, we don't require ConfigurationProperties to find the Vitro home + * directory, so we don't throw errors if there is no ConfigurationProperties. + */ +public class DeveloperSettingsStub extends DeveloperSettings { + /** + * Factory method. Create the stub and set it into the ServletContext. + */ + public static void set(ServletContext ctx) { + ctx.setAttribute(ATTRIBUTE_NAME, new DeveloperSettingsStub(ctx)); + } + + protected DeveloperSettingsStub(ServletContext ctx) { + super(ctx); + } + + @Override + protected void updateFromFile(ServletContext ctx) { + // Don't bother. + } + +} diff --git a/webapp/test/stubs/javax/servlet/ServletContextStub.java b/webapp/test/stubs/javax/servlet/ServletContextStub.java index 46e592d60..eb1e5eab7 100644 --- a/webapp/test/stubs/javax/servlet/ServletContextStub.java +++ b/webapp/test/stubs/javax/servlet/ServletContextStub.java @@ -21,6 +21,8 @@ import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import stubs.edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettingsStub; + /** * A simple stand-in for the {@link ServletContext}, for use in unit tests. */ @@ -36,6 +38,11 @@ public class ServletContextStub implements ServletContext { private final Map mockResources = new HashMap(); private final Map realPaths = new HashMap(); + public ServletContextStub() { + // Assume that unit tests won't want to use Developer mode. + DeveloperSettingsStub.set(this); + } + public void setContextPath(String contextPath) { if (contextPath == null) { throw new NullPointerException("contextPath may not be null."); From 0e2ae52c5a695f162c8797bbaa54ef74e6e32ec2 Mon Sep 17 00:00:00 2001 From: brianjlowe Date: Mon, 25 Nov 2013 16:17:19 -0500 Subject: [PATCH 14/18] fixes duplication bug when a faux has the same label as its "parent" --- .../individual/GroupedPropertyList.java | 80 ++++--------------- 1 file changed, 17 insertions(+), 63 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java index 1240285c3..8235202a6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java @@ -81,7 +81,7 @@ public class GroupedPropertyList extends BaseTemplateModel { // save applicable ranges before deduping to filter later populatedObjectPropertyList = dedupe(populatedObjectPropertyList); - + Collection additions = ApplicationConfigurationOntologyUtils .getAdditionalFauxSubpropertiesForList( populatedObjectPropertyList, subject, vreq); @@ -121,13 +121,7 @@ public class GroupedPropertyList extends BaseTemplateModel { if (editing) { mergeAllPossibleDataProperties(propertyList); } - -// Not currently necessary since the language-specific version is now added -// during the merge -// if (editing) { -// propertyList = correctLanguageForProperties(propertyList); -// } - + sort(propertyList); // Put the list into groups @@ -176,35 +170,6 @@ public class GroupedPropertyList extends BaseTemplateModel { return filteredAdditions; } - // Use the language-filtering WebappDaoFactory to get the right version of - // each property. When editing, the methods that add to the property list - // are blissfully (and intentionally) language-unaware. - private List correctLanguageForProperties(List properties) { - List languageCorrectedProps = new ArrayList(); - for (Property p : properties) { - Property correctedProp = null; - if (p instanceof ObjectProperty) { - ObjectProperty op = (ObjectProperty) p; - correctedProp = wdf.getObjectPropertyDao() - .getObjectPropertyByURIs(op.getURI(), - op.getDomainVClassURI(), op.getRangeVClassURI()); - } else if (p instanceof DataProperty) { - correctedProp = wdf.getDataPropertyDao() - .getDataPropertyByURI(((DataProperty) p).getURI()); - } else { - log.warn("Ignoring " + p.getURI() + " which is neither an " + - "ObjectProperty nor a DatatypeProperty."); - } - if (correctedProp != null) { - languageCorrectedProps.add(correctedProp); - } else { - log.error("Unable to retrieve property " + p.getURI() + - " using the WebappDaoFactory associated with the request."); - } - } - return languageCorrectedProps; - } - // It's possible that an object property retrieved in the call to getPopulatedObjectPropertyList() // is now empty of statements, because if not editing, some statements without a linked individual // are not retrieved by the query. (See elements in queries.) @@ -281,16 +246,17 @@ public class GroupedPropertyList extends BaseTemplateModel { continue; } boolean addToList = true; - int opIndex = 0; for(ObjectProperty op : populatedObjectPropertyList) { - if(redundant(op, piOp)) { + RedundancyReason reason = redundant(op, piOp); + if(reason != null) { addToList = false; - if (moreRestrictiveRange(piOp, op, wadf)) { - propertyList = replaceOpWithPiOpInList(piOp, op, opIndex, propertyList); + if (reason == RedundancyReason.LABEL_AND_URI_MATCH + && moreRestrictiveRange(piOp, op, wadf)) { + op.setRangeVClassURI(piOp.getRangeVClassURI()); + op.setRangeVClass(piOp.getRangeVClass()); } break; } - opIndex++; } if(addToList) { propertyList.add(piOp); @@ -315,6 +281,10 @@ public class GroupedPropertyList extends BaseTemplateModel { return propertyList; } + private enum RedundancyReason { + LABEL_AND_URI_MATCH, LABEL_URI_DOMAIN_AND_RANGE_MATCH + } + private boolean moreRestrictiveRange(ObjectProperty piOp, ObjectProperty op, WebappDaoFactory wadf) { if(piOp.getRangeVClassURI() == null) { @@ -327,25 +297,9 @@ public class GroupedPropertyList extends BaseTemplateModel { } } - private List replaceOpWithPiOpInList(ObjectProperty piOp, - ObjectProperty op, int opIndex, List propertyList) { - - List returnList = new ArrayList(); - int index = 0; - for(Property p : propertyList) { - if(index == opIndex /* p.equals(op) */) { - returnList.add(piOp); - } else { - returnList.add(p); - } - index++; - } - return returnList; - } - - private boolean redundant(ObjectProperty op, ObjectProperty op2) { + private RedundancyReason redundant(ObjectProperty op, ObjectProperty op2) { if (op2.getURI() == null) { - return false; + return null; } boolean uriMatches = (op.getURI() != null && op.getURI().equals(op2.getURI())); @@ -360,7 +314,7 @@ public class GroupedPropertyList extends BaseTemplateModel { labelMatches = true; } if(uriMatches && labelMatches) { - return true; + return RedundancyReason.LABEL_AND_URI_MATCH; } if(op.getDomainVClassURI() == null) { if(op2.getDomainVClassURI() == null) { @@ -377,9 +331,9 @@ public class GroupedPropertyList extends BaseTemplateModel { rangeMatches = true; } if (uriMatches && domainMatches && rangeMatches) { - return true; + return RedundancyReason.LABEL_URI_DOMAIN_AND_RANGE_MATCH; } - return false; + return null; } private void addObjectPropertyToPropertyList(String propertyUri, String domainUri, String rangeUri, From 27c8d4410e48cb21e3a304321dbb9ce5508a7a05 Mon Sep 17 00:00:00 2001 From: j2blake Date: Tue, 26 Nov 2013 10:22:37 -0500 Subject: [PATCH 15/18] VIVO-530 Fix logic bug in authorization Required actions from the DataGetters were being ignored. --- .../vitro/webapp/controller/freemarker/PageController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java index 785bb031b..a98434bc0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java @@ -58,10 +58,12 @@ public class PageController extends FreemarkerHttpServlet{ if( pageActs == null && dgActs == null){ return Actions.AUTHORIZED; - }else if( pageActs == null && dgActs != null ){ + }else if( pageActs == null ){ return dgActs; + }else if( dgActs == null ){ + return pageActs; }else{ - return pageActs; + return pageActs.and(dgActs); } } catch (Exception e) { From 60102fbf28f1ddc57da4c74d602a59db7d6dd564 Mon Sep 17 00:00:00 2001 From: j2blake Date: Tue, 26 Nov 2013 16:02:52 -0500 Subject: [PATCH 16/18] VIVO-530 Adjust SPARQL Update API --- .../utilities/testrunner/test-user-model.owl | 116 +++++----- .../rdf/auth/everytime/permission_config.n3 | 9 +- .../auth/permissions/SimplePermission.java | 7 +- .../webapp/auth/policy/PolicyHelper.java | 6 +- .../api/SparqlUpdateApiController.java | 201 ++++++++++++++++++ .../controller/freemarker/PageController.java | 3 +- .../webapp/servlet/setup/ThemeInfoSetup.java | 4 +- .../webapp/utils/dataGetter/SparqlUpdate.java | 96 --------- webapp/web/WEB-INF/web.xml | 10 + .../body/menupage/page-sparqlUpdateTest.ftl | 12 -- 10 files changed, 286 insertions(+), 178 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java delete mode 100644 webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl index a0acf6afa..a84b13707 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl @@ -1,58 +1,58 @@ - - - - - testAdmin@cornell.edu - testAdmin - Test - Admin - DC647EB65E6711E155375218212B3964 - ACTIVE - 1 - 0 - - - - - johnCurator@cornell.edu - johnCurator - John - Curator - DC647EB65E6711E155375218212B3964 - ACTIVE - 1 - 0 - - - - - sallyEditor@cornell.edu - sallyEditor - Sally - Editor - DC647EB65E6711E155375218212B3964 - ACTIVE - 1 - 0 - - - - - joeUser@cornell.edu - joeUser - Joe - User - DC647EB65E6711E155375218212B3964 - ACTIVE - 1 - 0 - - - - + + + + + testAdmin@cornell.edu + testAdmin + Test + Admin + DC647EB65E6711E155375218212B3964 + ACTIVE + 1 + 0 + + + + + johnCurator@cornell.edu + johnCurator + John + Curator + DC647EB65E6711E155375218212B3964 + ACTIVE + 1 + 0 + + + + + sallyEditor@cornell.edu + sallyEditor + Sally + Editor + DC647EB65E6711E155375218212B3964 + ACTIVE + 1 + 0 + + + + + joeUser@cornell.edu + joeUser + Joe + User + DC647EB65E6711E155375218212B3964 + ACTIVE + 1 + 0 + + + + diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index c4a88018a..e0b1c1960 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -12,6 +12,7 @@ auth:ADMIN # ADMIN-only permissions auth:hasPermission simplePermission:AccessSpecialDataModels ; + auth:hasPermission simplePermission:EnableDeveloperPanel ; auth:hasPermission simplePermission:LoginDuringMaintenance ; auth:hasPermission simplePermission:ManageMenus ; auth:hasPermission simplePermission:ManageProxies ; @@ -23,8 +24,12 @@ auth:ADMIN auth:hasPermission simplePermission:UseAdvancedDataToolsPages ; auth:hasPermission simplePermission:UseMiscellaneousAdminPages ; auth:hasPermission simplePermission:UseSparqlQueryPage ; - auth:hasPermission simplePermission:PageViewableAdmin ; - auth:hasPermission simplePermission:EnableDeveloperPanel ; + auth:hasPermission simplePermission:PageViewableAdmin ; + + # Uncomment the following permission line to enable the SPARQL update API. + # Before enabling, be sure that the URL api/sparqlUpdate is secured by SSH, + # so passwords will not be sent in clear text. +# auth:hasPermission simplePermission:UseSparqlUpdateApi ; # permissions for CURATOR and above. auth:hasPermission simplePermission:EditOntology ; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index 33c609c5b..0484638bd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -38,6 +38,8 @@ public class SimplePermission extends Permission { NAMESPACE + "EditOwnAccount"); public static final SimplePermission EDIT_SITE_INFORMATION = new SimplePermission( NAMESPACE + "EditSiteInformation"); + public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission( + NAMESPACE + "EnableDeveloperPanel"); public static final SimplePermission LOGIN_DURING_MAINTENANCE = new SimplePermission( NAMESPACE + "LoginDuringMaintenance"); public static final SimplePermission MANAGE_MENUS = new SimplePermission( @@ -76,9 +78,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"); - + public static final SimplePermission USE_SPARQL_UPDATE_API = new SimplePermission( + NAMESPACE + "UseSparqlUpdateApi"); // ---------------------------------------------------------------------- // These instances are "catch all" permissions to cover poorly defined diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java index d8a606efb..b1250a855 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java @@ -83,11 +83,11 @@ public class PolicyHelper { } try{ - Authenticator basicAuth = new BasicAuthenticator(req); - UserAccount user = basicAuth.getAccountForInternalAuth( email ); + Authenticator auth = Authenticator.getInstance(req); + UserAccount user = auth.getAccountForInternalAuth( email ); log.debug("userAccount is " + user==null?"null":user.getUri() ); - if( ! basicAuth.isCurrentPassword( user, password ) ){ + if( ! auth.isCurrentPassword( user, password ) ){ log.debug(String.format("UNAUTHORIZED, password not accepted for %s, account URI: %s", user.getEmailAddress(), user.getUri())); return false; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java new file mode 100644 index 000000000..60a0e6af0 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java @@ -0,0 +1,201 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.api; + +import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; +import static javax.servlet.http.HttpServletResponse.SC_OK; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.hp.hpl.jena.query.Dataset; +import com.hp.hpl.jena.update.GraphStore; +import com.hp.hpl.jena.update.GraphStoreFactory; +import com.hp.hpl.jena.update.UpdateAction; +import com.hp.hpl.jena.update.UpdateFactory; +import com.hp.hpl.jena.update.UpdateRequest; + +import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; +import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder; + +/** + * This extends HttpServlet instead of VitroHttpServlet because we want to have + * full control over the response: + *
      + *
    • No redirecting to the login page if not authorized
    • + *
    • No redirecting to the home page on insufficient authorization
    • + *
    • No support for GET or HEAD requests, only POST.
    • + *
    + * + * So these responses will be produced: + * + *
    + * 200 Success
    + * 400 Failed to parse SPARQL update
    + * 400 SPARQL update must specify a GRAPH URI.
    + * 403 username/password combination is not valid
    + * 403 Account is not authorized
    + * 405 Method not allowed
    + * 500 Unknown error
    + * 
    + */ +public class SparqlUpdateApiController extends HttpServlet { + private static final Log log = LogFactory + .getLog(SparqlUpdateApiController.class); + + private static final Actions REQUIRED_ACTIONS = SimplePermission.USE_SPARQL_UPDATE_API.ACTIONS; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + try { + checkAuthorization(req); + UpdateRequest parsed = parseUpdateString(req); + executeUpdate(req, parsed); + do200response(resp); + } catch (AuthException e) { + do403response(resp, e); + } catch (ParseException e) { + do400response(resp, e); + } catch (Exception e) { + do500response(resp, e); + } + } + + private void checkAuthorization(HttpServletRequest req) + throws AuthException { + String email = req.getParameter("email"); + String password = req.getParameter("password"); + + Authenticator auth = Authenticator.getInstance(req); + UserAccount account = auth.getAccountForInternalAuth(email); + if (!auth.isCurrentPassword(account, password)) { + log.debug("Invalid: '" + email + "'/'" + password + "'"); + throw new AuthException("email/password combination is not valid"); + } + + if (!PolicyHelper.isAuthorizedForActions(req, email, password, + REQUIRED_ACTIONS)) { + log.debug("Not authorized: '" + email + "'"); + throw new AuthException("Account is not authorized"); + } + + log.debug("Authorized for '" + email + "'"); + } + + private UpdateRequest parseUpdateString(HttpServletRequest req) + throws ParseException { + String update = req.getParameter("update"); + if (StringUtils.isBlank(update)) { + log.debug("No update parameter."); + throw new ParseException("No 'update' parameter."); + } + + if (!StringUtils.containsIgnoreCase(update, "GRAPH")) { + if (log.isDebugEnabled()) { + log.debug("No GRAPH uri in '" + update + "'"); + } + throw new ParseException("SPARQL update must specify a GRAPH URI."); + } + + try { + return UpdateFactory.create(update); + } catch (Exception e) { + log.debug("Problem parsing", e); + throw new ParseException("Failed to parse SPARQL update", e); + } + } + + private void executeUpdate(HttpServletRequest req, UpdateRequest parsed) { + ServletContext ctx = req.getSession().getServletContext(); + VitroRequest vreq = new VitroRequest(req); + + IndexBuilder.getBuilder(ctx).pause(); + try { + Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService()); + GraphStore graphStore = GraphStoreFactory.create(ds); + UpdateAction.execute(parsed, graphStore); + } finally { + IndexBuilder.getBuilder(ctx).unpause(); + } + } + + private void do200response(HttpServletResponse resp) throws IOException { + doResponse(resp, SC_OK, "SPARQL update accepted."); + } + + private void do403response(HttpServletResponse resp, AuthException e) + throws IOException { + doResponse(resp, SC_FORBIDDEN, e.getMessage()); + } + + private void do400response(HttpServletResponse resp, ParseException e) + throws IOException { + if (e.getCause() == null) { + doResponse(resp, SC_BAD_REQUEST, e.getMessage()); + } else { + doResponse(resp, SC_BAD_REQUEST, e.getMessage(), e.getCause()); + } + } + + private void do500response(HttpServletResponse resp, Exception e) + throws IOException { + doResponse(resp, SC_INTERNAL_SERVER_ERROR, "Unknown error", e); + } + + private void doResponse(HttpServletResponse resp, int statusCode, + String message) throws IOException { + resp.setStatus(statusCode); + PrintWriter writer = resp.getWriter(); + writer.println("

    " + statusCode + " " + message + "

    "); + } + + private void doResponse(HttpServletResponse resp, int statusCode, + String message, Throwable e) throws IOException { + resp.setStatus(statusCode); + PrintWriter writer = resp.getWriter(); + writer.println("

    " + statusCode + " " + message + "

    "); + writer.println("
    ");
    +		e.printStackTrace(writer);
    +		writer.println("
    "); + } + + // ---------------------------------------------------------------------- + // Helper classes + // ---------------------------------------------------------------------- + + private static class AuthException extends Exception { + private AuthException(String message) { + super(message); + } + } + + private static class ParseException extends Exception { + private ParseException(String message) { + super(message); + } + + private ParseException(String message, Throwable cause) { + super(message, cause); + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java index a98434bc0..fe08c7d95 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java @@ -67,8 +67,7 @@ public class PageController extends FreemarkerHttpServlet{ } } catch (Exception e) { - // TODO Auto-generated catch block - log.debug(e); + log.warn(e); return Actions.UNAUTHORIZED; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java index 6b917c43f..5ccea3bfd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java @@ -73,8 +73,8 @@ public class ThemeInfoSetup implements ServletContextListener { ApplicationBean.themeInfo = new ThemeInfo(themesBaseDir, defaultThemeName, themeNames); - ss.info(this, ", current theme: " + currentThemeName - + "default theme: " + defaultThemeName + ", available themes: " + ss.info(this, "current theme: " + currentThemeName + + ", default theme: " + defaultThemeName + ", available themes: " + themeNames); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java deleted file mode 100644 index 7bb33194b..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java +++ /dev/null @@ -1,96 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.dataGetter; - -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.ServletContext; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.hp.hpl.jena.query.Dataset; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.update.GraphStore; -import com.hp.hpl.jena.update.GraphStoreFactory; -import com.hp.hpl.jena.update.UpdateAction; - -import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; -import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; -import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; -import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequiresActions; -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; -import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder; - -/** - * Handle a SPARQL Update request. This uses Jena ARQ and the RDFServiceDataset to - * evaluate a SPARQL Update with the RDFService. - * - * The reason to make this a DataGettere was to allow configuration in RDF of this - * service. - */ -public class SparqlUpdate implements DataGetter, RequiresActions{ - - private static final Log log = LogFactory.getLog(SparqlUpdate.class); - - VitroRequest vreq; - ServletContext context; - - public SparqlUpdate( - VitroRequest vreq, Model displayModel, String dataGetterURI ) { - if( vreq == null ) - throw new IllegalArgumentException("VitroRequest may not be null."); - this.vreq = vreq; - this.context = vreq.getSession().getServletContext(); - } - - - /** - * Gets the update from the request and then executes it on - * the RDFService. - */ - @Override - public Map getData( Map valueMap ) { - HashMap data = new HashMap(); - - String update = vreq.getParameter("update"); - - if( update != null && !update.trim().isEmpty()){ - try{ - IndexBuilder.getBuilder(context).pause(); - Dataset ds = new RDFServiceDataset( vreq.getUnfilteredRDFService() ); - GraphStore graphStore = GraphStoreFactory.create(ds); - log.warn("The SPARQL update is '"+vreq.getParameter("update")+"'"); - UpdateAction.parseExecute( vreq.getParameter("update") , graphStore ); - }finally{ - IndexBuilder.getBuilder(context).unpause(); - } - - } - - data.put("bodyTemplate", "page-sparqlUpdateTest.ftl"); - return data; - } - - - /** - * Check if this request is authorized by the email/password. - * If not do normal authorization. - */ - @Override - public Actions requiredActions(VitroRequest vreq) { - String email = vreq.getParameter("email"); - String password = vreq.getParameter("password"); - - boolean isAuth = PolicyHelper.isAuthorizedForActions(vreq, - email, password, SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS); - - if( isAuth ) - return Actions.AUTHORIZED; - else - return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS; - } - -} diff --git a/webapp/web/WEB-INF/web.xml b/webapp/web/WEB-INF/web.xml index 3a5377bda..1d393a7b9 100644 --- a/webapp/web/WEB-INF/web.xml +++ b/webapp/web/WEB-INF/web.xml @@ -1025,6 +1025,16 @@ /admin/sparqlquery + + SparqlUpdateApi + edu.cornell.mannlib.vitro.webapp.controller.api.SparqlUpdateApiController + + + + SparqlUpdateApi + /api/sparqlUpdate + + primitiveRdfEdit edu.cornell.mannlib.vitro.webapp.controller.edit.PrimitiveRdfEdit diff --git a/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl b/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl deleted file mode 100644 index 17274f829..000000000 --- a/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl +++ /dev/null @@ -1,12 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -

    SPARQL Update Test

    - -

    This is an expermental SPARQL update service.

    - -
    -

    - - -

    -
    \ No newline at end of file From f981fd2c532a799da7a1360ca2f277494a7fabe1 Mon Sep 17 00:00:00 2001 From: j2blake Date: Sun, 1 Dec 2013 19:30:57 -0500 Subject: [PATCH 17/18] VIVO-588 Fixed a typo to fix the deadlocked threads. --- .../edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java index 23e300013..fd333560b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java @@ -537,13 +537,14 @@ public class PageDaoJena extends JenaBaseDao implements PageDao { List actions = new ArrayList(); Model dModel = getOntModelSelector().getDisplayModel(); + dModel.enterCriticalSection(false); try{ QueryExecution qe = QueryExecutionFactory.create( requiredActionsQuery, dModel, initialBindings); actions = executeQueryToList( qe ); qe.close(); }finally{ - dModel.enterCriticalSection(false); + dModel.leaveCriticalSection(); } return actions; } From 356e86de704e5ef74f7a3ddb9d5fc9027fdc59a7 Mon Sep 17 00:00:00 2001 From: j2blake Date: Sun, 1 Dec 2013 19:32:43 -0500 Subject: [PATCH 18/18] =?UTF-8?q?VIVO-581=20Don=E2=80=99t=20check=20for=20?= =?UTF-8?q?=E2=80=9Cregular=20files=E2=80=9D,=20just=20check=20for=20?= =?UTF-8?q?=E2=80=9Cnot=20directory=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webapp/freemarker/loader/FreemarkerTemplateLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java index 2ae556862..0bef38b30 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java @@ -285,7 +285,7 @@ public class FreemarkerTemplateLoader implements TemplateLoader { } public boolean fileQualifies(Path path) { - return Files.isRegularFile(path) && Files.isReadable(path); + return Files.isReadable(path) && !Files.isDirectory(path); } public SortedSet getMatches() {