Merge pull request #115 from awoods/sprint-search

Update sprint-search to current develop branch
This commit is contained in:
hudajkhan 2019-04-03 12:43:52 -04:00 committed by GitHub
commit 48df6fa4ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 179 additions and 61 deletions

View file

@ -1,4 +1,7 @@
# What is Vitro? # 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 a general-purpose web-based ontology and instance editor with customizable public browsing.
Vitro is an integrated ontology editor and semantic web application. Vitro is an integrated ontology editor and semantic web application.
@ -13,7 +16,7 @@ With Vitro, you can:
* Search your data with Apache Solr * Search your data with Apache Solr
Vitro was originally developed at Cornell University, and is used as the core of the popular 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/).

View file

@ -4,6 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.config;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; 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.ServletContext;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
@ -25,10 +32,11 @@ public class ConfigurationPropertiesSmokeTests implements
.getLog(ConfigurationPropertiesSmokeTests.class); .getLog(ConfigurationPropertiesSmokeTests.class);
private static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace"; 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_SELECTABLE = "languages.selectableLocales";
private static final String PROPERTY_LANGUAGE_FORCE = "languages.forceLocale"; private static final String PROPERTY_LANGUAGE_FORCE = "languages.forceLocale";
private static final String PROPERTY_LANGUAGE_FILTER = "RDFService.languageFilter"; 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_TIME = "argon2.time";
private static final String PROPERTY_ARGON2_MEMORY = "argon2.memory"; private static final String PROPERTY_ARGON2_MEMORY = "argon2.memory";
private static final String PROPERTY_ARGON2_PARALLELISM = "argon2.parallelism"; private static final String PROPERTY_ARGON2_PARALLELISM = "argon2.parallelism";
@ -41,8 +49,9 @@ public class ConfigurationPropertiesSmokeTests implements
checkDefaultNamespace(ctx, props, ss); checkDefaultNamespace(ctx, props, ss);
checkMultipleRPFs(ctx, props, ss); checkMultipleRPFs(ctx, props, ss);
checkLanguages(props, ss); checkLanguages(ctx, props, ss);
checkEncryptionParameters(props, ss); checkEncryptionParameters(props, ss);
} }
/** /**
@ -110,10 +119,7 @@ public class ConfigurationPropertiesSmokeTests implements
* languages and force language. Shouldn't build with language unless * languages and force language. Shouldn't build with language unless
* language filtering is enabled. * language filtering is enabled.
*/ */
private void checkLanguages(ConfigurationProperties props, StartupStatus ss) { private void checkLanguages(ServletContext ctx, ConfigurationProperties props, StartupStatus ss) {
String buildString = props.getProperty(PROPERTY_LANGUAGE_BUILD);
boolean buildWithLanguages = StringUtils.isNotBlank(buildString);
String selectString = props.getProperty(PROPERTY_LANGUAGE_SELECTABLE); String selectString = props.getProperty(PROPERTY_LANGUAGE_SELECTABLE);
boolean selectableLanguages = StringUtils.isNotBlank(selectString); boolean selectableLanguages = StringUtils.isNotBlank(selectString);
@ -121,16 +127,44 @@ public class ConfigurationPropertiesSmokeTests implements
boolean forceLanguage = StringUtils.isNotBlank(forceString); boolean forceLanguage = StringUtils.isNotBlank(forceString);
String filterString = props.getProperty(PROPERTY_LANGUAGE_FILTER, String filterString = props.getProperty(PROPERTY_LANGUAGE_FILTER,
"true"); "false");
boolean languageFilter = Boolean.valueOf(filterString); boolean languageFilter = Boolean.valueOf(filterString);
String i18nDirPath = ctx.getRealPath("/i18n");
if (selectableLanguages && !buildWithLanguages) { if (i18nDirPath == null) {
ss.warning(this, String.format("Problem with Language setup - " throw new IllegalStateException(
+ "runtime.properties specifies a " "Application does not have an /i18n directory.");
+ "list of selectable languages (%s = %s), but " }
+ "build.properties did not include any languages with %s",
PROPERTY_LANGUAGE_SELECTABLE, selectString, List<String> i18nNames = null;
PROPERTY_LANGUAGE_BUILD));
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<String> 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) { if (selectableLanguages && forceLanguage) {
@ -142,17 +176,31 @@ public class ConfigurationPropertiesSmokeTests implements
PROPERTY_LANGUAGE_SELECTABLE, selectString)); PROPERTY_LANGUAGE_SELECTABLE, selectString));
} }
if (buildWithLanguages && !languageFilter) { if (selectableLanguages && !languageFilter) {
ss.warning(this, String.format("Problem with Language setup - " 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 " + "languages (%s = %s), but runtime.properties has "
+ "disabled language filtering (%s = %s). This will " + "disabled language filtering (%s = %s). This will "
+ "likely result in a mix of languages in the " + "likely result in a mix of languages in the "
+ "application.", PROPERTY_LANGUAGE_BUILD, buildString, + "application.", PROPERTY_LANGUAGE_SELECTABLE, selectString,
PROPERTY_LANGUAGE_FILTER, filterString)); PROPERTY_LANGUAGE_FILTER, filterString));
} }
} }
/** Create a list of the names of available language files. */
private List<String> 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. * 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 '" ss.fatal(this, "runtime.properties does not contain a value for '"
+ name + "'"); + name + "'");
return; return;
} }
} }

View file

@ -17,6 +17,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import edu.cornell.mannlib.vitro.webapp.dao.jena.MenuDaoJena;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -461,7 +462,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
protected MainMenu getDisplayModelMenu(VitroRequest vreq){ protected MainMenu getDisplayModelMenu(VitroRequest vreq){
String url = vreq.getRequestURI().substring(vreq.getContextPath().length()); 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 // NIHVIVO-3307: we need this here instead of FreemarkerConfiguration.java so that updates to

View file

@ -3,10 +3,19 @@ package edu.cornell.mannlib.vitro.webapp.dao;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu;
import javax.servlet.ServletRequest;
public interface MenuDao { public interface MenuDao {
/** /**
* @param url - url of request for setting active menu items. This should start with a / and not have the context path. * @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 * 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);
} }

View file

@ -5,9 +5,11 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.query.Query; import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution; import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory; 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.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu;
import javax.servlet.ServletRequest;
public class MenuDaoJena extends JenaBaseDao implements MenuDao { public class MenuDaoJena extends JenaBaseDao implements MenuDao {
private static final Log log = LogFactory.getLog(MenuDaoJena.class); private static final Log log = LogFactory.getLog(MenuDaoJena.class);
@ -68,6 +72,10 @@ public class MenuDaoJena extends JenaBaseDao implements MenuDao {
return getMenu( getOntModelSelector().getDisplayModel(), url ); return getMenu( getOntModelSelector().getDisplayModel(), 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){ protected MainMenu getMenu(Model displayModel, String url){
//setup query parameters //setup query parameters

View file

@ -365,10 +365,8 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
@Override @Override
public ObjectPropertyStatementDao getObjectPropertyStatementDao() { public ObjectPropertyStatementDao getObjectPropertyStatementDao() {
if( objectPropertyStatementDao == null ) if( objectPropertyStatementDao == null )
// TODO supply a valid RDFService as the first argument if we keep this
// implementation
objectPropertyStatementDao = new ObjectPropertyStatementDaoJena( objectPropertyStatementDao = new ObjectPropertyStatementDaoJena(
null, dwf, this); rdfService, dwf, this);
return objectPropertyStatementDao; return objectPropertyStatementDao;
} }

View file

@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.Consts;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
@ -495,8 +496,8 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
protected void executeUpdate(String updateString) throws RDFServiceException { protected void executeUpdate(String updateString) throws RDFServiceException {
try { try {
HttpPost meth = new HttpPost(updateEndpointURI); HttpPost meth = new HttpPost(updateEndpointURI);
meth.addHeader("Content-Type", "application/x-www-form-urlencoded"); meth.addHeader("Content-Type", "application/x-www-form-urlencoded; charset="+Consts.UTF_8);
meth.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("update", updateString)))); meth.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("update", updateString)),Consts.UTF_8));
HttpContext context = getContext(meth); HttpContext context = getContext(meth);
HttpResponse response = context != null ? httpClient.execute(meth, context) : httpClient.execute(meth); HttpResponse response = context != null ? httpClient.execute(meth, context) : httpClient.execute(meth);
try { try {
@ -510,6 +511,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
EntityUtils.consume(response.getEntity()); EntityUtils.consume(response.getEntity());
} }
} catch (Exception e) { } catch (Exception e) {
log.debug("update string: \n" + updateString);
throw new RDFServiceException("Unable to perform change set update", e); throw new RDFServiceException("Unable to perform change set update", e);
} }
} }

View file

@ -176,8 +176,6 @@ public class UpdateDocumentWorkUnit implements Runnable {
classGroupUris.add(classGroupUri); classGroupUris.add(classGroupUri);
} }
addToAlltext(doc, clz.getName());
Float boost = clz.getSearchBoost(); Float boost = clz.getSearchBoost();
if (boost != null) { if (boost != null) {
doc.setDocumentBoost(doc.getDocumentBoost() + boost); doc.setDocumentBoost(doc.getDocumentBoost() + boost);

View file

@ -197,7 +197,12 @@ public class CustomListViewConfigFile {
*/ */
NodeList doomed = element.getElementsByTagName(tagName); NodeList doomed = element.getElementsByTagName(tagName);
for (int i = doomed.getLength() - 1; i >= 0; i--) { 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));
} }
} }

View file

@ -10,15 +10,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.log4j.Level; import org.apache.log4j.Level;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -119,16 +122,33 @@ public class AuthenticateTest extends AbstractTestClass {
private static final String NO_USER = ""; private static final String NO_USER = "";
private static final String NO_MSG = ""; 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<UserAccount> 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 @Before
public void setup() throws Exception { public void setup() throws Exception {
I18nStub.setup(); I18nStub.setup();
authenticatorFactory = new AuthenticatorStub.Factory(); authenticatorFactory = new AuthenticatorStub.Factory();
authenticator = authenticatorFactory.getInstance(request); authenticator = authenticatorFactory.getInstance(request);
authenticator.addUser(createUserFromUserInfo(NEW_DBA));
authenticator.addUser(createUserFromUserInfo(OLD_DBA)); for (UserAccount account : userAccounts) {
authenticator.addUser(createUserFromUserInfo(OLD_SELF)); authenticator.addUser(account);
authenticator.addUser(createUserFromUserInfo(OLD_STRANGER)); }
authenticator.setAssociatedUri(OLD_SELF.username, authenticator.setAssociatedUri(OLD_SELF.username,
"old_self_associated_uri"); "old_self_associated_uri");
@ -143,10 +163,9 @@ public class AuthenticateTest extends AbstractTestClass {
userAccountsDao = new UserAccountsDaoStub(); userAccountsDao = new UserAccountsDaoStub();
userAccountsDao.addPermissionSet(adminPermissionSet); userAccountsDao.addPermissionSet(adminPermissionSet);
userAccountsDao.addUser(createUserFromUserInfo(NEW_DBA)); for (UserAccount account : userAccounts) {
userAccountsDao.addUser(createUserFromUserInfo(OLD_DBA)); userAccountsDao.addUser(account);
userAccountsDao.addUser(createUserFromUserInfo(OLD_SELF)); }
userAccountsDao.addUser(createUserFromUserInfo(OLD_STRANGER));
individualDao = new IndividualDaoStub(); individualDao = new IndividualDaoStub();
@ -186,7 +205,7 @@ public class AuthenticateTest extends AbstractTestClass {
new HasPermissionFactory(servletContext)); new HasPermissionFactory(servletContext));
} }
private UserAccount createUserFromUserInfo(UserInfo userInfo) { private static UserAccount createUserFromUserInfo(UserInfo userInfo) {
UserAccount user = new UserAccount(); UserAccount user = new UserAccount();
user.setEmailAddress(userInfo.username); user.setEmailAddress(userInfo.username);
user.setUri(userInfo.uri); user.setUri(userInfo.uri);

View file

@ -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.dao.MenuDao;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu;
import javax.servlet.ServletRequest;
/** /**
* A minimal implementation of the MenuDao. * A minimal implementation of the MenuDao.
* *
@ -35,6 +37,11 @@ public class MenuDaoStub implements MenuDao {
return this.mainMenu; return this.mainMenu;
} }
@Override
public MainMenu getMainMenu(ServletRequest req, String url) {
return this.mainMenu;
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Un-implemented methods // Un-implemented methods
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View file

@ -64,7 +64,7 @@
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <version>3.8.0</version>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-dependency-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>

51
pom.xml
View file

@ -193,6 +193,30 @@
</plugins> </plugins>
</build> </build>
</profile> </profile>
<profile>
<!-- JDK 8 profile for the Error Prone plugin -->
<id>jdk8</id>
<activation>
<jdk>1.8</jdk>
</activation>
<properties>
<javac.version>9+181-r4173-1</javac.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<compilerArgs combine.children="append">
<arg>-J-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
<build> <build>
@ -204,23 +228,18 @@
<source>1.8</source> <source>1.8</source>
<target>1.8</target> <target>1.8</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<compilerId>javac-with-errorprone</compilerId> <compilerArgs>
<forceJavacCompilerUse>true</forceJavacCompilerUse> <arg>-XDcompilePolicy=simple</arg>
</configuration> <arg>-Xplugin:ErrorProne</arg>
<dependencies> </compilerArgs>
<dependency> <annotationProcessorPaths>
<groupId>org.codehaus.plexus</groupId> <path>
<artifactId>plexus-compiler-javac-errorprone</artifactId>
<version>2.8</version>
</dependency>
<!-- override plexus-compiler-javac-errorprone's dependency on
Error Prone with the latest version -->
<dependency>
<groupId>com.google.errorprone</groupId> <groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId> <artifactId>error_prone_core</artifactId>
<version>2.1.1</version> <version>2.3.2</version>
</dependency> </path>
</dependencies> </annotationProcessorPaths>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
@ -267,7 +286,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <version>3.8.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View file

@ -24,7 +24,7 @@
<hr class="hidden" /> <hr class="hidden" />
<div id="contentwrap"> <div id="contentwrap">
<#include "flash.html"> <#include "flash.ftl">
<div id="content"> <div id="content">
${body} ${body}