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 c6e9a3523..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 @@ -16,7 +16,7 @@ ACTIVE 1 0 - + @@ -28,7 +28,7 @@ ACTIVE 1 0 - + @@ -40,7 +40,7 @@ ACTIVE 1 0 - + @@ -52,7 +52,7 @@ ACTIVE 1 0 - + 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/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/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: + * + * + * 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/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/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java index 785bb031b..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 @@ -58,15 +58,16 @@ 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) { - // TODO Auto-generated catch block - log.debug(e); + log.warn(e); return Actions.UNAUTHORIZED; } } 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; } 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) { 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..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 @@ -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. @@ -276,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() { 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/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 { 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) { 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/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/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java index 822e434e4..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 @@ -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; @@ -159,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) { @@ -183,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/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/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, 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"); } } } 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/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."); 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/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/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/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/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 ") ; }); }); 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 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 diff --git a/webapp/web/templates/freemarker/lib/lib-properties.ftl b/webapp/web/templates/freemarker/lib/lib-properties.ftl index 95b467eda..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> @@ -231,6 +231,9 @@ name will be used as the label. --> <#local verboseDisplay = property.verboseDisplay!> <#if verboseDisplay?has_content>
    + <#if verboseDisplay.fauxProperty??> + a faux property of + ${verboseDisplay.localName} (${property.type?lower_case} property); order in group: ${verboseDisplay.displayRank}; 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 533de3d2c..82b2a0c80 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -6,27 +6,9 @@ <#macro showTextbox key> - + - - - <#if !settings.developerEnabled> <#elseif !settings.mayControl>
    @@ -38,40 +20,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