diff --git a/README.md b/README.md index 6b3234a1e..1125398ec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # What is Vitro? + +[![Build Status](https://travis-ci.org/vivo-project/Vitro.png?branch=develop)](https://travis-ci.org/vivo-project/Vitro) + Vitro is a general-purpose web-based ontology and instance editor with customizable public browsing. Vitro is an integrated ontology editor and semantic web application. @@ -13,7 +16,7 @@ With Vitro, you can: * Search your data with Apache Solr Vitro was originally developed at Cornell University, and is used as the core of the popular -research and scholarship portal, [VIVO](http://vivoweb.org) . +research and scholarship portal, [VIVO](https://duraspace.org/vivo/). -For more information, contact the [VIVO community](http://www.vivoweb.org/support/user-feedback). +For more information, contact the [VIVO community](https://duraspace.org/vivo/resources/contact/). diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java index a4a3b396e..bdfda1e7f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java @@ -4,6 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.config; import java.net.URI; import java.net.URISyntaxException; +import java.io.IOException; +import java.util.List; +import java.util.Arrays; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.util.stream.Collectors; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; @@ -25,10 +32,11 @@ public class ConfigurationPropertiesSmokeTests implements .getLog(ConfigurationPropertiesSmokeTests.class); private static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace"; - private static final String PROPERTY_LANGUAGE_BUILD = "languages.addToBuild"; private static final String PROPERTY_LANGUAGE_SELECTABLE = "languages.selectableLocales"; private static final String PROPERTY_LANGUAGE_FORCE = "languages.forceLocale"; private static final String PROPERTY_LANGUAGE_FILTER = "RDFService.languageFilter"; + private static final String VIVO_BUNDLE_PREFIX = "vivo_all_"; + private static final String VITRO_BUNDLE_PREFIX = "all_"; private static final String PROPERTY_ARGON2_TIME = "argon2.time"; private static final String PROPERTY_ARGON2_MEMORY = "argon2.memory"; private static final String PROPERTY_ARGON2_PARALLELISM = "argon2.parallelism"; @@ -41,8 +49,9 @@ public class ConfigurationPropertiesSmokeTests implements checkDefaultNamespace(ctx, props, ss); checkMultipleRPFs(ctx, props, ss); - checkLanguages(props, ss); + checkLanguages(ctx, props, ss); checkEncryptionParameters(props, ss); + } /** @@ -110,10 +119,7 @@ public class ConfigurationPropertiesSmokeTests implements * languages and force language. Shouldn't build with language unless * language filtering is enabled. */ - private void checkLanguages(ConfigurationProperties props, StartupStatus ss) { - String buildString = props.getProperty(PROPERTY_LANGUAGE_BUILD); - boolean buildWithLanguages = StringUtils.isNotBlank(buildString); - + private void checkLanguages(ServletContext ctx, ConfigurationProperties props, StartupStatus ss) { String selectString = props.getProperty(PROPERTY_LANGUAGE_SELECTABLE); boolean selectableLanguages = StringUtils.isNotBlank(selectString); @@ -121,16 +127,44 @@ public class ConfigurationPropertiesSmokeTests implements boolean forceLanguage = StringUtils.isNotBlank(forceString); String filterString = props.getProperty(PROPERTY_LANGUAGE_FILTER, - "true"); + "false"); boolean languageFilter = Boolean.valueOf(filterString); + String i18nDirPath = ctx.getRealPath("/i18n"); - if (selectableLanguages && !buildWithLanguages) { - ss.warning(this, String.format("Problem with Language setup - " - + "runtime.properties specifies a " - + "list of selectable languages (%s = %s), but " - + "build.properties did not include any languages with %s", - PROPERTY_LANGUAGE_SELECTABLE, selectString, - PROPERTY_LANGUAGE_BUILD)); + if (i18nDirPath == null) { + throw new IllegalStateException( + "Application does not have an /i18n directory."); + } + + List i18nNames = null; + + i18nNames = geti18nNames(i18nDirPath); + + log.debug("i18nNames: " + i18nNames); + + if (i18nNames.isEmpty()) { + ss.fatal(this, "The application found no files in '" + + i18nDirPath + + "' ."); + } + else { + ss.info(this, "Base language files loaded: " + i18nNames); + } + + /* Make sure language files exist for values in the selectableLocales propery. + The prefixes of vitro and vivo are hard coded into the app, + so we can assume the bundle names must have the same file format */ + if (selectableLanguages) { + List selectableLanguagesList = Arrays.asList(selectString.split("\\s*,\\s*")); + for (String language : selectableLanguagesList) { + String vivoBundle = VIVO_BUNDLE_PREFIX + language + ".properties"; + String vitroBundle = VITRO_BUNDLE_PREFIX + language + ".properties"; + if (!i18nNames.contains(vivoBundle) && !i18nNames.contains(vitroBundle)) { + ss.warning(this, language + " was found in the value for " + + PROPERTY_LANGUAGE_SELECTABLE + " but no corresponding " + + "language file was found."); + } + } } if (selectableLanguages && forceLanguage) { @@ -142,17 +176,31 @@ public class ConfigurationPropertiesSmokeTests implements PROPERTY_LANGUAGE_SELECTABLE, selectString)); } - if (buildWithLanguages && !languageFilter) { + if (selectableLanguages && !languageFilter) { ss.warning(this, String.format("Problem with Language setup - " - + "build.properties includes one or more additional " + + "languages.selectableLocales in runtime.properties " + + "includes one or more additional " + "languages (%s = %s), but runtime.properties has " + "disabled language filtering (%s = %s). This will " + "likely result in a mix of languages in the " - + "application.", PROPERTY_LANGUAGE_BUILD, buildString, + + "application.", PROPERTY_LANGUAGE_SELECTABLE, selectString, PROPERTY_LANGUAGE_FILTER, filterString)); } } + /** Create a list of the names of available language files. */ + private List geti18nNames(String i18nBaseDirPath) { + try { + return Files.walk(Paths.get(i18nBaseDirPath)) + .filter(Files::isRegularFile) + .map(Path::getFileName) + .map(p -> {return p.toString();}) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException("Failed to find language files", e); + } + } + /** * Fail if there are no config properties for the Argon2 encryption. */ @@ -170,6 +218,7 @@ public class ConfigurationPropertiesSmokeTests implements ss.fatal(this, "runtime.properties does not contain a value for '" + name + "'"); return; + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java index 765d5cabd..ab339e3d8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java @@ -17,6 +17,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import edu.cornell.mannlib.vitro.webapp.dao.jena.MenuDaoJena; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -461,7 +462,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { protected MainMenu getDisplayModelMenu(VitroRequest vreq){ String url = vreq.getRequestURI().substring(vreq.getContextPath().length()); - return vreq.getWebappDaoFactory().getMenuDao().getMainMenu(url); + return vreq.getWebappDaoFactory().getMenuDao().getMainMenu(vreq, url); } // NIHVIVO-3307: we need this here instead of FreemarkerConfiguration.java so that updates to diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/MenuDao.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/MenuDao.java index 2e930e26b..34d75c310 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/MenuDao.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/MenuDao.java @@ -3,10 +3,19 @@ package edu.cornell.mannlib.vitro.webapp.dao; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; +import javax.servlet.ServletRequest; + public interface MenuDao { /** * @param url - url of request for setting active menu items. This should start with a / and not have the context path. * These values will be checked against urlMapping. ex. /people or /home */ - public MainMenu getMainMenu( String url); + public MainMenu getMainMenu( String url); + + /** + * @param req - the ServletRequest for the current request + * @param url - url of request for setting active menu items. This should start with a / and not have the context path. + * These values will be checked against urlMapping. ex. /people or /home + */ + public MainMenu getMainMenu(ServletRequest req, String url); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJena.java index 72cef5077..ef3745dc9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJena.java @@ -5,9 +5,11 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.util.HashSet; import java.util.Set; +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.ontology.OntModel; import org.apache.jena.query.Query; import org.apache.jena.query.QueryExecution; import org.apache.jena.query.QueryExecutionFactory; @@ -23,6 +25,8 @@ import edu.cornell.mannlib.vitro.webapp.dao.MenuDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; +import javax.servlet.ServletRequest; + public class MenuDaoJena extends JenaBaseDao implements MenuDao { private static final Log log = LogFactory.getLog(MenuDaoJena.class); @@ -65,11 +69,15 @@ public class MenuDaoJena extends JenaBaseDao implements MenuDao { @Override public MainMenu getMainMenu( String url ) { - return getMenu( getOntModelSelector().getDisplayModel(), url ); + return getMenu( getOntModelSelector().getDisplayModel(), url ); } - - - protected MainMenu getMenu(Model displayModel, String url){ + + public MainMenu getMainMenu( ServletRequest req, String url ) { + OntModel displayModel = LanguageFilteringUtils.wrapOntModelInALanguageFilter(getOntModelSelector().getDisplayModel(), req ); + return getMenu(displayModel, url) ; + } + + protected MainMenu getMenu(Model displayModel, String url){ //setup query parameters QuerySolutionMap initialBindings = new QuerySolutionMap(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/WebappDaoFactoryJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/WebappDaoFactoryJena.java index fad97129e..84d2917c9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/WebappDaoFactoryJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/WebappDaoFactoryJena.java @@ -365,10 +365,8 @@ public class WebappDaoFactoryJena implements WebappDaoFactory { @Override public ObjectPropertyStatementDao getObjectPropertyStatementDao() { if( objectPropertyStatementDao == null ) - // TODO supply a valid RDFService as the first argument if we keep this - // implementation objectPropertyStatementDao = new ObjectPropertyStatementDaoJena( - null, dwf, this); + rdfService, dwf, this); return objectPropertyStatementDao; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java index db7e22237..1fd9c2442 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sparql/RDFServiceSparql.java @@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.http.Consts; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthenticationException; @@ -495,8 +496,8 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService { protected void executeUpdate(String updateString) throws RDFServiceException { try { HttpPost meth = new HttpPost(updateEndpointURI); - meth.addHeader("Content-Type", "application/x-www-form-urlencoded"); - meth.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("update", updateString)))); + meth.addHeader("Content-Type", "application/x-www-form-urlencoded; charset="+Consts.UTF_8); + meth.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("update", updateString)),Consts.UTF_8)); HttpContext context = getContext(meth); HttpResponse response = context != null ? httpClient.execute(meth, context) : httpClient.execute(meth); try { @@ -510,6 +511,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService { EntityUtils.consume(response.getEntity()); } } catch (Exception e) { + log.debug("update string: \n" + updateString); throw new RDFServiceException("Unable to perform change set update", e); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java index f6503ad7f..81dc58e6d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java @@ -176,8 +176,6 @@ public class UpdateDocumentWorkUnit implements Runnable { classGroupUris.add(classGroupUri); } - addToAlltext(doc, clz.getName()); - Float boost = clz.getSearchBoost(); if (boost != null) { doc.setDocumentBoost(doc.getDocumentBoost() + boost); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewConfigFile.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewConfigFile.java index cf8e0ea4c..56aae2bd2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewConfigFile.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewConfigFile.java @@ -197,7 +197,12 @@ public class CustomListViewConfigFile { */ NodeList doomed = element.getElementsByTagName(tagName); for (int i = doomed.getLength() - 1; i >= 0; i--) { - element.removeChild(doomed.item(i)); + /* + * As the node to be removed may be at an arbitrary depth in the DOM tree, + * we need to get the parent of the node that we are trying to remove, + * and then remove the child from there. + */ + doomed.item(i).getParentNode().removeChild(doomed.item(i)); } } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java index db160e00a..c6d011c8f 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java @@ -10,15 +10,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Level; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -119,16 +122,33 @@ public class AuthenticateTest extends AbstractTestClass { private static final String NO_USER = ""; private static final String NO_MSG = ""; + /** + * Due to increased overhead of password hashing, create a list of UserAccount objects once + * when the class is cloaded. + * + * These prepared user accounts will then be (re)used to populate the authenticator stubs prior to each test + */ + private static List userAccounts = new ArrayList<>(); + + @BeforeClass + public static void prepareUserAccounts() { + userAccounts.add(createUserFromUserInfo(NEW_DBA)); + userAccounts.add(createUserFromUserInfo(OLD_DBA)); + userAccounts.add(createUserFromUserInfo(OLD_SELF)); + userAccounts.add(createUserFromUserInfo(OLD_STRANGER)); + } + @Before public void setup() throws Exception { I18nStub.setup(); authenticatorFactory = new AuthenticatorStub.Factory(); authenticator = authenticatorFactory.getInstance(request); - authenticator.addUser(createUserFromUserInfo(NEW_DBA)); - authenticator.addUser(createUserFromUserInfo(OLD_DBA)); - authenticator.addUser(createUserFromUserInfo(OLD_SELF)); - authenticator.addUser(createUserFromUserInfo(OLD_STRANGER)); + + for (UserAccount account : userAccounts) { + authenticator.addUser(account); + } + authenticator.setAssociatedUri(OLD_SELF.username, "old_self_associated_uri"); @@ -143,10 +163,9 @@ public class AuthenticateTest extends AbstractTestClass { userAccountsDao = new UserAccountsDaoStub(); userAccountsDao.addPermissionSet(adminPermissionSet); - userAccountsDao.addUser(createUserFromUserInfo(NEW_DBA)); - userAccountsDao.addUser(createUserFromUserInfo(OLD_DBA)); - userAccountsDao.addUser(createUserFromUserInfo(OLD_SELF)); - userAccountsDao.addUser(createUserFromUserInfo(OLD_STRANGER)); + for (UserAccount account : userAccounts) { + userAccountsDao.addUser(account); + } individualDao = new IndividualDaoStub(); @@ -186,7 +205,7 @@ public class AuthenticateTest extends AbstractTestClass { new HasPermissionFactory(servletContext)); } - private UserAccount createUserFromUserInfo(UserInfo userInfo) { + private static UserAccount createUserFromUserInfo(UserInfo userInfo) { UserAccount user = new UserAccount(); user.setEmailAddress(userInfo.username); user.setUri(userInfo.uri); diff --git a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/MenuDaoStub.java b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/MenuDaoStub.java index 4f275659e..5d9476770 100644 --- a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/MenuDaoStub.java +++ b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/MenuDaoStub.java @@ -5,6 +5,8 @@ package stubs.edu.cornell.mannlib.vitro.webapp.dao; import edu.cornell.mannlib.vitro.webapp.dao.MenuDao; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; +import javax.servlet.ServletRequest; + /** * A minimal implementation of the MenuDao. * @@ -35,6 +37,11 @@ public class MenuDaoStub implements MenuDao { return this.mainMenu; } + @Override + public MainMenu getMainMenu(ServletRequest req, String url) { + return this.mainMenu; + } + // ---------------------------------------------------------------------- // Un-implemented methods // ---------------------------------------------------------------------- diff --git a/installer/pom.xml b/installer/pom.xml index 68ef4e20c..b861258f5 100644 --- a/installer/pom.xml +++ b/installer/pom.xml @@ -64,7 +64,7 @@ maven-compiler-plugin - 3.5.1 + 3.8.0 maven-dependency-plugin diff --git a/pom.xml b/pom.xml index f5139538b..2274ee995 100644 --- a/pom.xml +++ b/pom.xml @@ -193,6 +193,30 @@ + + + jdk8 + + 1.8 + + + 9+181-r4173-1 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + true + + -J-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar + + + + + + @@ -204,23 +228,18 @@ 1.8 1.8 UTF-8 - javac-with-errorprone - true + + -XDcompilePolicy=simple + -Xplugin:ErrorProne + + + + com.google.errorprone + error_prone_core + 2.3.2 + + - - - org.codehaus.plexus - plexus-compiler-javac-errorprone - 2.8 - - - - com.google.errorprone - error_prone_core - 2.1.1 - - org.codehaus.mojo @@ -267,7 +286,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.8.0 org.apache.maven.plugins diff --git a/webapp/src/main/webapp/templates/freemarker/page/page.ftl b/webapp/src/main/webapp/templates/freemarker/page/page.ftl index 47c43791c..60dcf1016 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/page.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/page.ftl @@ -24,7 +24,7 @@
- <#include "flash.html"> + <#include "flash.ftl">
${body}