diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..aa9d3a4aa --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: Build + +on: [ push, pull_request, workflow_dispatch ] + +jobs: + build: + runs-on: ubuntu-latest + + env: + MAVEN_OPTS: -Xmx1024M + + steps: + - uses: actions/checkout@v2 + + - name: Maven Cache + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-cache-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-cache-m2- + + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Maven Build + run: mvn clean install diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ce9b09b50..000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: java -dist: trusty -sudo: false - -jdk: - - openjdk8 - - oraclejdk8 - -env: - # Give Maven 1GB of memory to work with - - MAVEN_OPTS=-Xmx1024M - -cache: - directories: - - .autoconf - - $HOME/.m2 - -install: - - "echo 'Skipping install stage, dependencies will be downloaded during build and test stages.'" - -script: - - "mvn clean package -Dmaven.test.skip=false" diff --git a/README.md b/README.md index 1125398ec..d65138c6a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # What is Vitro? -[![Build Status](https://travis-ci.org/vivo-project/Vitro.png?branch=develop)](https://travis-ci.org/vivo-project/Vitro) +[![Build](https://github.com/vivo-project/Vitro/workflows/Build/badge.svg)](https://github.com/vivo-project/Vitro/actions?query=workflow%3ABuild) Vitro is a general-purpose web-based ontology and instance editor with customizable public browsing. diff --git a/api/pom.xml b/api/pom.xml index c486a2ee1..a122a4355 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-api - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT jar org.vivoweb vitro-project - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. @@ -66,7 +66,7 @@ org.vivoweb vitro-dependencies - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationSetup.java index 03798f6c4..a8fd8aa2f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationSetup.java @@ -62,6 +62,7 @@ public class ApplicationSetup implements ServletContextListener { private void locateApplicationConfigFile() { Path path = this.vitroHomeDir.getPath().resolve(APPLICATION_SETUP_PATH); + if (!Files.exists(path)) { throw new IllegalStateException("'" + path + "' does not exist."); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java index e72436152..816bf7d51 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java @@ -37,7 +37,14 @@ public class RootUserPolicy implements PolicyIface { private static final Log log = LogFactory.getLog(RootUserPolicy.class); private static final String PROPERTY_ROOT_USER_EMAIL = "rootUser.emailAddress"; + /* + * UQAM Add-Feature For parameterization of rootUser + */ + private static final String PROPERTY_ROOT_USER_PASSWORD = "rootUser.password"; + private static final String PROPERTY_ROOT_USER_PASSWORD_CHANGE_REQUIRED = "rootUser.passwordChangeRequired"; + private static final String ROOT_USER_INITIAL_PASSWORD = "rootPassword"; + private static final String ROOT_USER_INITIAL_PASSWORD_CHANGE_REQUIRED = "true"; /** * This is the entire policy. If you are a root user, you are authorized. @@ -150,10 +157,12 @@ public class RootUserPolicy implements PolicyIface { ua.setEmailAddress(configuredRootUser); ua.setFirstName("root"); ua.setLastName("user"); + // UQAM Add-Feature using getRootPasswordFromConfig() ua.setArgon2Password(Authenticator.applyArgon2iEncoding( - ROOT_USER_INITIAL_PASSWORD)); + getRootPasswordFromConfig())); ua.setMd5Password(""); - ua.setPasswordChangeRequired(true); + // UQAM Add-Feature using getRootPasswdChangeRequiredFromConfig() + ua.setPasswordChangeRequired(getRootPasswdChangeRequiredFromConfig().booleanValue()); ua.setStatus(Status.ACTIVE); ua.setRootUser(true); @@ -191,7 +200,31 @@ public class RootUserPolicy implements PolicyIface { ss.warning(this, "For security, " + "it is best to delete unneeded root user accounts."); } + /* + * UQAM Add-Feature + * Add for getting rootUser.password property value from runtime.properties + */ + private String getRootPasswordFromConfig() { + String passwd = ConfigurationProperties.getBean(ctx).getProperty( + PROPERTY_ROOT_USER_PASSWORD); + if (passwd == null) { + passwd = ROOT_USER_INITIAL_PASSWORD; + } + return passwd; + } + /* + * UQAM Add-Feature + * Add for getting rootUser.passwordChangeRequired property value from runtime.properties + */ + private Boolean getRootPasswdChangeRequiredFromConfig() { + String passwdCR = ConfigurationProperties.getBean(ctx).getProperty( + PROPERTY_ROOT_USER_PASSWORD_CHANGE_REQUIRED); + if (passwdCR == null) { + passwdCR = ROOT_USER_INITIAL_PASSWORD_CHANGE_REQUIRED; + } + return new Boolean(passwdCR); + } @Override public void contextDestroyed(ServletContextEvent sce) { // Nothing to destroy diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java index c083d44f3..563b1c9b4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java @@ -20,6 +20,7 @@ public class ApplicationBean { public final static int VIVO_SEARCHBOX_SIZE = 20; private final static String DEFAULT_APPLICATION_NAME = "Vitro"; + private final static String DEFAULT_APPLICATION_AVAILABLE_LANGS_FILE = "available-langs"; private final static String DEFAULT_ROOT_LOGOTYPE_IMAGE = ""; private final static int DEFAULT_ROOT_LOGOTYPE_WIDTH = 0; private final static int DEFAULT_ROOT_LOGOTYPE_HEIGHT = 0; @@ -33,6 +34,7 @@ public class ApplicationBean { private boolean initialized = false; private String sessionIdStr = null; private String applicationName = DEFAULT_APPLICATION_NAME; + private String availableLangsFile = DEFAULT_APPLICATION_AVAILABLE_LANGS_FILE; private String rootLogotypeImage = DEFAULT_ROOT_LOGOTYPE_IMAGE; private int rootLogotypeWidth = DEFAULT_ROOT_LOGOTYPE_WIDTH; @@ -52,6 +54,7 @@ public class ApplicationBean { output += " initialized from DB: [" + initialized + "]\n"; output += " session id: [" + sessionIdStr + "]\n"; output += " application name: [" + applicationName + "]\n"; + output += " available langs file: [" + availableLangsFile + "]\n"; output += " root logotype image: [" + rootLogotypeImage + "]\n"; output += " root logotype width: [" + rootLogotypeWidth + "]\n"; output += " root logotype height: [" + rootLogotypeHeight + "]\n"; @@ -177,6 +180,10 @@ public class ApplicationBean { return ""; } + public String getAvailableLangsFile() { + return availableLangsFile; + } + /** * Directory to find the images. Subdirectories include css, jsp and site_icons. * Example: "themes/enhanced/" diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java index 7b9d49792..7b5581885 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java @@ -7,17 +7,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import org.apache.commons.lang3.RandomStringUtils; /** * Information about the account of a user. URI, email, password, etc. * - * The "password link expires hash" is just a string that is derived from the - * value in the passwordLinkExpires field. It doesn't have to be a hash, and - * there is no need for it to be cryptographic, but it seems embarrassing to - * just send the value as a clear string. There is no real need for security - * here, except that a brute force attack would allow someone to change the - * password on an account that they know has a password change pending. */ public class UserAccount { public static final int MIN_PASSWORD_LENGTH = 6; @@ -52,6 +46,7 @@ public class UserAccount { private String md5Password = ""; // Never null. private String oldPassword = ""; // Never null. private long passwordLinkExpires = 0L; // Never negative. + private String emailKey = ""; private boolean passwordChangeRequired = false; private int loginCount = 0; // Never negative. @@ -133,15 +128,27 @@ public class UserAccount { return passwordLinkExpires; } - public String getPasswordLinkExpiresHash() { - return limitStringLength(8, Authenticator.applyArgon2iEncoding(String - .valueOf(passwordLinkExpires))); - } - public void setPasswordLinkExpires(long passwordLinkExpires) { this.passwordLinkExpires = Math.max(0, passwordLinkExpires); } + public void generateEmailKey() { + boolean useLetters = true; + boolean useNumbers = true; + int length = 64; + emailKey = RandomStringUtils.random(length, useLetters, useNumbers); + } + + public void setEmailKey(String emailKey) { + if (emailKey != null) { + this.emailKey = emailKey; + } + } + + public String getEmailKey() { + return emailKey; + } + public boolean isPasswordChangeRequired() { return passwordChangeRequired; } @@ -247,6 +254,7 @@ public class UserAccount { + (", oldPassword=" + oldPassword) + (", argon2password=" + argon2Password) + (", passwordLinkExpires=" + passwordLinkExpires) + + (", emailKey =" + emailKey) + (", passwordChangeRequired=" + passwordChangeRequired) + (", externalAuthOnly=" + externalAuthOnly) + (", loginCount=" + loginCount) + (", status=" + status) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java index 49fe5e81d..ee27feef3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java @@ -109,7 +109,7 @@ public abstract class ConfigurationProperties { throw new NullPointerException("bean may not be null."); } context.setAttribute(ATTRIBUTE_NAME, bean); - log.info(bean); + log.debug(bean); } /** Package access, so unit tests can call it. */ diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java index 773f43e87..54d0ac90d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java @@ -73,7 +73,7 @@ public class ConfigurationPropertiesSetup implements ServletContextListener { vitroHomeDir, vitroHomeDirConfig); File runtimePropertiesFile = locateRuntimePropertiesFile( - vitroHomeDir, vitroHomeDirConfig, ss); + vitroHomeDir, vitroHomeDirConfig, ss); stream = new FileInputStream(runtimePropertiesFile); Map preempts = createPreemptiveProperties( @@ -118,7 +118,6 @@ public class ConfigurationPropertiesSetup implements ServletContextListener { } } - private File locateRuntimePropertiesFile(File vitroHomeDir, File vitroHomeDirConfig, StartupStatus ss) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java index 6caadf24c..d878ae315 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java @@ -249,6 +249,7 @@ public class UserAccountsSelector { user.setMd5Password(ifLiteralPresent(solution, "md5pwd", "")); user.setArgon2Password(ifLiteralPresent(solution, "a2pwd", "")); user.setPasswordLinkExpires(ifLongPresent(solution, "expire", 0L)); + user.setEmailKey(ifLiteralPresent(solution, "emailKey", "")); user.setLoginCount(ifIntPresent(solution, "count", 0)); user.setLastLoginTime(ifLongPresent(solution, "lastLogin", 0)); user.setStatus(parseStatus(solution, "status", null)); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java index bdbc5dbce..7fc4181da 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java @@ -156,6 +156,7 @@ public class UserAccountsAddPage extends UserAccountsPage { u.setOldPassword(""); u.setPasswordChangeRequired(false); u.setPasswordLinkExpires(0); + u.setEmailKey(""); u.setLoginCount(0); u.setLastLoginTime(0L); u.setStatus(Status.INACTIVE); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java index 307ccf9f6..036d314e2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java @@ -84,6 +84,7 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { u.setStatus(Status.ACTIVE); } else { u.setPasswordLinkExpires(figureExpirationDate().getTime()); + u.generateEmailKey(); u.setStatus(Status.INACTIVE); } } @@ -119,10 +120,8 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { private String buildCreatePasswordLink() { try { String email = page.getAddedAccount().getEmailAddress(); - String hash = page.getAddedAccount() - .getPasswordLinkExpiresHash(); - String relativeUrl = UrlBuilder.getUrl(CREATE_PASSWORD_URL, - "user", email, "key", hash); + String key = page.getAddedAccount().getEmailKey(); + String relativeUrl = UrlBuilder.getUrl(CREATE_PASSWORD_URL, "user", email, "key", key); URL context = new URL(vreq.getRequestURL().toString()); URL url = new URL(context, relativeUrl); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java index 42dd4102d..13e1ab0dd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java @@ -274,6 +274,7 @@ public class UserAccountsEditPage extends UserAccountsPage { userAccount.setOldPassword(""); userAccount.setPasswordChangeRequired(false); userAccount.setPasswordLinkExpires(0L); + userAccount.setEmailKey(""); } if (isRootUser()) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java index 708f11e66..d9ee8aa14 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java @@ -82,6 +82,7 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage { protected void setAdditionalProperties(UserAccount u) { if (resetPassword && !page.isExternalAuthOnly()) { u.setPasswordLinkExpires(figureExpirationDate().getTime()); + u.generateEmailKey(); } } @@ -121,10 +122,8 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage { private String buildResetPasswordLink() { try { String email = page.getUpdatedAccount().getEmailAddress(); - String hash = page.getUpdatedAccount() - .getPasswordLinkExpiresHash(); - String relativeUrl = UrlBuilder.getUrl(RESET_PASSWORD_URL, - "user", email, "key", hash); + String key = page.getUpdatedAccount().getEmailKey(); + String relativeUrl = UrlBuilder.getUrl(RESET_PASSWORD_URL, "user", email, "key", key); URL context = new URL(vreq.getRequestURL().toString()); URL url = new URL(context, relativeUrl); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java index b9515d4c7..68daa2d67 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java @@ -36,6 +36,7 @@ public class UserAccountsCreatePasswordPage extends userAccount.setArgon2Password(Authenticator.applyArgon2iEncoding(newPassword)); userAccount.setMd5Password(""); userAccount.setPasswordLinkExpires(0L); + userAccount.setEmailKey(""); userAccount.setPasswordChangeRequired(false); userAccount.setStatus(Status.ACTIVE); userAccountsDao.updateUserAccount(userAccount); @@ -53,6 +54,11 @@ public class UserAccountsCreatePasswordPage extends protected String passwordChangeNotPendingMessage() { return i18n.text("account_already_activated", userEmail); } + + @Override + protected String passwordChangeInavlidKeyMessage() { + return i18n.text("password_change_invalid_key", userEmail); + } @Override protected String templateName() { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java index fc11f665d..ffb34c754 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java @@ -195,6 +195,7 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { u.setExternalAuthId(externalAuthId); u.setPasswordChangeRequired(false); u.setPasswordLinkExpires(0); + u.setEmailKey(""); u.setExternalAuthOnly(true); u.setLoginCount(0); u.setStatus(Status.ACTIVE); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java index 057098eea..ca895cab8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java @@ -159,6 +159,7 @@ public abstract class UserAccountsMyAccountPageStrategy extends userAccount.setMd5Password(""); userAccount.setPasswordChangeRequired(false); userAccount.setPasswordLinkExpires(0L); + userAccount.setEmailKey(""); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java index 3923c17b2..d4cc56f03 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java @@ -103,12 +103,12 @@ public abstract class UserAccountsPasswordBasePage extends UserAccountsPage { return; } - String expectedKey = userAccount.getPasswordLinkExpiresHash(); - if (!key.equals(expectedKey)) { + String expectedKey = userAccount.getEmailKey(); + if (key.isEmpty() || !key.equals(expectedKey)) { log.warn("Password request for '" + userEmail + "' is bogus: key (" + key + ") doesn't match expected key (" + expectedKey + ")"); - bogusMessage = passwordChangeNotPendingMessage(); + bogusMessage = passwordChangeInavlidKeyMessage(); return; } @@ -153,7 +153,7 @@ public abstract class UserAccountsPasswordBasePage extends UserAccountsPage { body.put("minimumLength", UserAccount.MIN_PASSWORD_LENGTH); body.put("maximumLength", UserAccount.MAX_PASSWORD_LENGTH); body.put("userAccount", userAccount); - body.put("key", userAccount.getPasswordLinkExpiresHash()); + body.put("key", userAccount.getEmailKey()); body.put("newPassword", newPassword); body.put("confirmPassword", confirmPassword); body.put("formUrls", buildUrlsMap()); @@ -176,6 +176,8 @@ public abstract class UserAccountsPasswordBasePage extends UserAccountsPage { protected abstract String alreadyLoggedInMessage(String currentUserEmail); protected abstract String passwordChangeNotPendingMessage(); + + protected abstract String passwordChangeInavlidKeyMessage(); protected abstract String templateName(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java index 712df1b40..f865cbe94 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java @@ -55,6 +55,11 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage protected String passwordChangeNotPendingMessage() { return i18n.text("password_change_not_pending", userEmail); } + + @Override + protected String passwordChangeInavlidKeyMessage() { + return i18n.text("password_change_invalid_key", userEmail); + } @Override protected String templateName() { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java index 096263dab..5369d2626 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java @@ -17,6 +17,8 @@ import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -75,8 +77,8 @@ public class SparqlQueryController extends FreemarkerHttpServlet { private static final String[] SAMPLE_QUERY = { // "", // "#", // - "# This example query gets 20 geographic locations", // - "# and (if available) their labels", // + "i18n:sparql_query_description_0", // + "i18n:sparql_query_description_1", // "#", // "SELECT ?geoLocation ?label", // "WHERE", // @@ -193,9 +195,11 @@ public class SparqlQueryController extends FreemarkerHttpServlet { @Override protected ResponseValues processRequest(VitroRequest vreq) throws Exception { + I18nBundle i18n = I18n.bundle(vreq); + Map bodyMap = new HashMap<>(); - bodyMap.put("sampleQuery", buildSampleQuery(buildPrefixList(vreq))); - bodyMap.put("title", "SPARQL Query"); + bodyMap.put("sampleQuery", buildSampleQuery(i18n, buildPrefixList(vreq))); + bodyMap.put("title", i18n.text("sparql_query_title")); bodyMap.put("submitUrl", UrlBuilder.getUrl("admin/sparqlquery")); return new TemplateResponseValues(TEMPLATE_NAME, bodyMap); } @@ -222,7 +226,7 @@ public class SparqlQueryController extends FreemarkerHttpServlet { return prefixList; } - private String buildSampleQuery(List prefixList) { + private String buildSampleQuery(I18nBundle i18n, List prefixList) { StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); @@ -230,6 +234,10 @@ public class SparqlQueryController extends FreemarkerHttpServlet { writer.println(p); } for (String line : SAMPLE_QUERY) { + if (line.startsWith("i18n:")) { + // Get i18n translation + line = i18n.text(line.substring("i18n:".length())); + } writer.println(line); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java index f6f95081b..34fc6a01d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java @@ -134,6 +134,7 @@ public class BasicAuthenticator extends Authenticator { userAccount.setMd5Password(""); userAccount.setPasswordChangeRequired(false); userAccount.setPasswordLinkExpires(0L); + userAccount.setEmailKey(""); getUserAccountsDao().updateUserAccount(userAccount); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index ffc18f805..65b8a2310 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -108,7 +108,9 @@ public class LoginRedirector { throws IOException { try { DisplayMessage.setMessage(request, assembleWelcomeMessage()); - response.sendRedirect(getRedirectionUriForLoggedInUser()); + String redirectUrl = getRedirectionUriForLoggedInUser(); + log.debug("Sending redirect to path: " + redirectUrl); + response.sendRedirect(redirectUrl); } catch (IOException e) { log.debug("Problem with re-direction", e); response.sendRedirect(getApplicationHomePageUrl()); @@ -175,21 +177,13 @@ public class LoginRedirector { } } - /** - * The application home page can be overridden by an attribute in the - * ServletContext. Further, it can either be an absolute URL, or it can be - * relative to the application. Weird. - */ private String getApplicationHomePageUrl() { - String contextRedirect = (String) session.getServletContext() - .getAttribute("postLoginRequest"); - if (contextRedirect != null) { - if (contextRedirect.indexOf(":") == -1) { - return request.getContextPath() + contextRedirect; - } else { - return contextRedirect; - } + String contextPath = request.getContextPath(); + if (contextPath.equals("")) { + return "/"; + } + else { + return contextPath; } - return request.getContextPath(); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/Classes2ClassesRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/Classes2ClassesRetryController.java index d4417e6a3..d51d90452 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/Classes2ClassesRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/Classes2ClassesRetryController.java @@ -43,7 +43,7 @@ public class Classes2ClassesRetryController extends BaseEditController { action = epo.getAction(); } - VClassDao vcDao = ModelAccess.on(getServletContext()).getWebappDaoFactory().getVClassDao(); + VClassDao vcDao = ModelAccess.on(request).getWebappDaoFactory().getVClassDao(); epo.setDataAccessObject(vcDao); Classes2Classes objectForEditing = new Classes2Classes(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ClassgroupRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ClassgroupRetryController.java index 5c1535489..82b618665 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ClassgroupRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ClassgroupRetryController.java @@ -51,8 +51,7 @@ public class ClassgroupRetryController extends BaseEditController { action = epo.getAction(); } - VClassGroupDao cgDao = ModelAccess.on( - getServletContext()).getWebappDaoFactory().getVClassGroupDao(); + VClassGroupDao cgDao = ModelAccess.on(request).getWebappDaoFactory().getVClassGroupDao(); epo.setDataAccessObject(cgDao); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropRetryController.java index c4b04c08b..70d47a80a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropRetryController.java @@ -50,11 +50,12 @@ public class DatapropRetryController extends BaseEditController { //create an EditProcessObject for this and put it in the session EditProcessObject epo = super.createEpo(request); + epo.setImplementationClass(DataProperty.class); epo.setBeanClass(DataProperty.class); VitroRequest vreq = new VitroRequest(request); - WebappDaoFactory wadf = ModelAccess.on(getServletContext()).getWebappDaoFactory(); + WebappDaoFactory wadf = ModelAccess.on(vreq).getWebappDaoFactory(); DatatypeDao dDao = wadf.getDatatypeDao(); DataPropertyDao dpDao = wadf.getDataPropertyDao(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java index 941c6e7f6..9f4d27092 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java @@ -106,7 +106,7 @@ public class FauxPropertyRetryController extends BaseEditController { this.epo = epo; - this.fpDao = ModelAccess.on(ctx).getWebappDaoFactory() + this.fpDao = ModelAccess.on(req).getWebappDaoFactory() .getFauxPropertyDao(); } @@ -114,6 +114,8 @@ public class FauxPropertyRetryController extends BaseEditController { void populate() { epo.setDataAccessObject(fpDao); epo.setAction(determineAction()); + epo.setImplementationClass(FauxProperty.class); + epo.setBeanClass(FauxProperty.class); if (epo.getUseRecycledBean()) { beanForEditing = (FauxProperty) epo.getNewBean(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/NamespacePrefixOperationController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/NamespacePrefixOperationController.java index 356569c54..2223c69e6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/NamespacePrefixOperationController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/NamespacePrefixOperationController.java @@ -71,7 +71,7 @@ public class NamespacePrefixOperationController extends BaseEditController { if (request.getParameter("_cancel") == null) { - OntModel ontModel = ModelAccess.on(getServletContext()).getOntModel(); + OntModel ontModel = ModelAccess.on(req).getOntModel(); String namespaceStr = request.getParameter("namespace"); String prefixStr = request.getParameter("prefix"); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyGroupRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyGroupRetryController.java index d84b15db5..3ae22e5c1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyGroupRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyGroupRetryController.java @@ -51,7 +51,7 @@ public class PropertyGroupRetryController extends BaseEditController { } PropertyGroupDao pgDao = ModelAccess.on( - getServletContext()).getWebappDaoFactory().getPropertyGroupDao(); + req).getWebappDaoFactory().getPropertyGroupDao(); epo.setDataAccessObject(pgDao); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java index 5e35fb369..909ebeff9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java @@ -53,6 +53,7 @@ public class PropertyRetryController extends BaseEditController { /*for testing*/ ObjectProperty testMask = new ObjectProperty(); + epo.setImplementationClass(ObjectProperty.class); epo.setBeanClass(ObjectProperty.class); epo.setBeanMask(testMask); @@ -64,7 +65,7 @@ public class PropertyRetryController extends BaseEditController { action = epo.getAction(); } - ObjectPropertyDao propDao = ModelAccess.on(getServletContext()).getWebappDaoFactory().getObjectPropertyDao(); + ObjectPropertyDao propDao = ModelAccess.on(req).getWebappDaoFactory().getObjectPropertyDao(); epo.setDataAccessObject(propDao); OntologyDao ontDao = request.getUnfilteredWebappDaoFactory().getOntologyDao(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/RestrictionOperationController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/RestrictionOperationController.java index f704b5b39..26956be74 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/RestrictionOperationController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/RestrictionOperationController.java @@ -48,7 +48,7 @@ public class RestrictionOperationController extends BaseEditController { String defaultLandingPage = getDefaultLandingPage(request); try { - OntModel ontModel = ModelAccess.on(getServletContext()) + OntModel ontModel = ModelAccess.on(req) .getOntModel(TBOX_ASSERTIONS); HashMap epoHash = null; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java index 340a846e4..a9b018670 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java @@ -48,7 +48,7 @@ public class VclassEditController extends BaseEditController { EditProcessObject epo = super.createEpo(request, FORCE_NEW); request.setAttribute("epoKey", epo.getKey()); - VClassDao vcwDao = ModelAccess.on(getServletContext()).getWebappDaoFactory(ASSERTIONS_ONLY).getVClassDao(); + VClassDao vcwDao = ModelAccess.on(req).getWebappDaoFactory(ASSERTIONS_ONLY).getVClassDao(); VClass vcl = (VClass)vcwDao.getVClassByURI(request.getParameter("uri")); if (vcl == null) { @@ -152,8 +152,8 @@ public class VclassEditController extends BaseEditController { request.setAttribute("formSelect",formSelect); // if supported, we want to show only the asserted superclasses and subclasses. - VClassDao vcDao = ModelAccess.on(getServletContext()).getWebappDaoFactory(ASSERTIONS_ONLY).getVClassDao(); - VClassDao displayVcDao = ModelAccess.on(getServletContext()).getWebappDaoFactory().getVClassDao(); + VClassDao vcDao = ModelAccess.on(req).getWebappDaoFactory(ASSERTIONS_ONLY).getVClassDao(); + VClassDao displayVcDao = ModelAccess.on(req).getWebappDaoFactory().getVClassDao(); List superVClasses = getVClassesForURIList( vcDao.getSuperClassURIs(vcl.getURI(),false), displayVcDao); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java index ee00bc64e..326b4fb1d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java @@ -66,11 +66,10 @@ public class VclassRetryController extends BaseEditController { action = epo.getAction(); } - WebappDaoFactory wadf = ModelAccess.on(getServletContext()).getWebappDaoFactory(); + WebappDaoFactory wadf = ModelAccess.on(req).getWebappDaoFactory(); VClassDao vcwDao = wadf.getVClassDao(); epo.setDataAccessObject(vcwDao); - VClassGroupDao cgDao = wadf.getVClassGroupDao(); OntologyDao oDao = wadf.getOntologyDao(); VClass vclassForEditing = null; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/NamespacesListingController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/NamespacesListingController.java index 05acd18ad..79bc5f071 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/NamespacesListingController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/NamespacesListingController.java @@ -37,7 +37,7 @@ public class NamespacesListingController extends BaseEditController { VitroRequest vrequest = new VitroRequest(request); - OntModel ontModel = ModelAccess.on(getServletContext()).getOntModel(); + OntModel ontModel = ModelAccess.on(vrequest).getOntModel(); ArrayList results = new ArrayList(); request.setAttribute("results",results); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/RestrictionsListingController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/RestrictionsListingController.java index 02b176969..746185621 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/RestrictionsListingController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/jena/RestrictionsListingController.java @@ -54,7 +54,7 @@ public class RestrictionsListingController extends BaseEditController { epo = super.createEpo(request); - OntModel ontModel = ModelAccess.on(getServletContext()).getOntModel(); + OntModel ontModel = ModelAccess.on(vrequest).getOntModel(); ObjectPropertyDao opDao = vrequest.getUnfilteredWebappDaoFactory().getObjectPropertyDao(); VClassDao vcDao = vrequest.getUnfilteredWebappDaoFactory().getVClassDao(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java index 0ee20f2fe..fcf655e36 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java @@ -3,6 +3,8 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,6 +44,8 @@ public class BrowseController extends FreemarkerHttpServlet { List groups = null; VClassGroupsForRequest vcgc = VClassGroupCache.getVClassGroups(vreq); groups =vcgc.getGroups(); + Collections.sort(groups, publicNameComparator); +// sortGroupListByPublicName(groups); List vcgroups = new ArrayList(groups.size()); for (VClassGroup group : groups) { vcgroups.add(new VClassGroupTemplateModel(group)); @@ -50,4 +54,35 @@ public class BrowseController extends FreemarkerHttpServlet { return new TemplateResponseValues(templateName, body); } + public Comparator publicNameComparator = new Comparator() { + + public int compare(VClassGroup s1, VClassGroup s2) { + String groupName1 = s1.getPublicName().toUpperCase(); + String groupName2 = s2.getPublicName().toUpperCase(); + + //ascending order + return groupName1.compareTo(groupName2); + + //descending order + //return groupName2.compareTo(groupName1); + }}; + +// +// public void sortGroupListByPublicName(List groupList) { +// groupList.sort(new Comparator() { +// public int compare(VClassGroup first, VClassGroup second) { +// if (first != null) { +// if (second != null) { +// return (first.getDisplayRank() - second.getDisplayRank()); +// } else { +// log.error("error--2nd VClassGroup is null in VClassGroupDao.getGroupList().compare()"); +// } +// } else { +// log.error("error--1st VClassGroup is null in VClassGroupDao.getGroupList().compare()"); +// } +// return 0; +// } +// }); +// } + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeletePropertyController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeletePropertyController.java index c62a2c87a..18fb4f39f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeletePropertyController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeletePropertyController.java @@ -10,6 +10,9 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.ResourceFactory; + import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; @@ -25,6 +28,8 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUti import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.N3EditUtils; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.web.URLEncoder; + + /* * Custom deletion controller to which deletion requests from default property form are sent. May be replaced * later with additional features in process rdf form controller or alternative location. @@ -61,14 +66,14 @@ public class DeletePropertyController extends FreemarkerHttpServlet { private String getRedirectUrl(VitroRequest vreq) { - // TODO Auto-generated method stub + // TODO Auto-generated method stub String subjectUri = EditConfigurationUtils.getSubjectUri(vreq); String predicateUri = EditConfigurationUtils.getPredicateUri(vreq); - int hashIndex = predicateUri.lastIndexOf("#"); - String localName = predicateUri.substring(hashIndex + 1); + Property prop = ResourceFactory.createProperty(predicateUri); + String localName = prop.getLocalName(); String redirectUrl = "/entity?uri=" + URLEncoder.encode(subjectUri); - return redirectUrl + "#" + URLEncoder.encode(localName); - } + return redirectUrl + "#" + URLEncoder.encode(localName); + } private String handleErrors(VitroRequest vreq) { 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 bc7766e83..50f54e2e0 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 @@ -8,6 +8,7 @@ import static javax.mail.Message.RecipientType.TO; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.Charset; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -17,7 +18,6 @@ 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; @@ -93,6 +93,8 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { throws IOException, ServletException { super.doGet(request,response); +//UQAM-Optimization set for UTF-8 + response.setCharacterEncoding("UTF-8"); VitroRequest vreq = new VitroRequest(request); ResponseValues responseValues = null; @@ -103,8 +105,8 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { if (!isAuthorizedToDisplayPage(request, response, requiredActions(vreq))) { return; } - responseValues = processRequest(vreq); + doResponse(vreq, response, responseValues); } catch (Throwable e) { @@ -256,7 +258,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { protected void doTemplate(VitroRequest vreq, HttpServletResponse response, ResponseValues values) throws TemplateProcessingException { - Map templateDataModel = new HashMap(); + Map templateDataModel = new HashMap(); templateDataModel.putAll(getPageTemplateValues(vreq)); // Add the values that we got from the subcontroller processRequest() method, and merge to the template. @@ -276,8 +278,15 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { // is specified in the main page template. bodyString = ""; } + templateDataModel.put("body", bodyString); + String lang = vreq.getLocale().getLanguage(); + if (!vreq.getLocale().getCountry().isEmpty()) { + lang += "-" + vreq.getLocale().getCountry(); + } + templateDataModel.put("country", lang); + // Tell the template and any directives it uses that we're processing a page template. templateDataModel.put("templateType", PAGE_TEMPLATE_TYPE); @@ -462,7 +471,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { protected MainMenu getDisplayModelMenu(VitroRequest vreq){ String url = vreq.getRequestURI().substring(vreq.getContextPath().length()); - return vreq.getWebappDaoFactory().getMenuDao().getMainMenu(vreq, url); + return vreq.getWebappDaoFactory().getMenuDao().getMainMenu(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/controller/freemarker/TemplateProcessingHelper.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java index 85d648f6b..c5439d4a2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java @@ -42,7 +42,7 @@ public class TemplateProcessingHelper { try { Environment env = template.createProcessingEnvironment(map, writer); - + // Define a setup template to be included by every page template String templateType = (String) map.get("templateType"); if (FreemarkerHttpServlet.PAGE_TEMPLATE_TYPE.equals(templateType)) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ViewLabelsServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ViewLabelsServlet.java index 1425b10f7..f68db8d56 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ViewLabelsServlet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ViewLabelsServlet.java @@ -11,28 +11,29 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; import org.apache.jena.rdf.model.Literal; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.dao.jena.QueryUtils; import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; /*Servlet to view all labels in various languages for individual*/ @@ -47,12 +48,13 @@ public class ViewLabelsServlet extends FreemarkerHttpServlet{ String subjectUri = vreq.getParameter("subjectUri"); body.put("subjectUri", subjectUri); try { - //Get all language codes/labels in the system, and this list is sorted by language name - List> locales = this.getLocales(vreq); + //the labels already added by the user + ArrayList existingLabels = this.getExistingLabels(subjectUri, vreq); + //Get all language codes/labels used in the list of existing labels + List> locales = this.getLocales(vreq, existingLabels); //Get code to label hashmap - we use this to get the language name for the language code returned in the rdf literal HashMap localeCodeToNameMap = this.getFullCodeToLanguageNameMap(locales); - //the labels already added by the user - ArrayList existingLabels = this.getExistingLabels(subjectUri, vreq); + //existing labels keyed by language name and each of the list of labels is sorted by language name HashMap> existingLabelsByLanguageName = this.getLabelsSortedByLanguageName(existingLabels, localeCodeToNameMap, vreq, subjectUri); //Get available locales for the drop down for adding a new label, also sorted by language name @@ -137,20 +139,26 @@ public class ViewLabelsServlet extends FreemarkerHttpServlet{ doGet(request, response); } - //get locales - public List> getLocales(VitroRequest vreq) { - List selectables = SelectedLocale.getSelectableLocales(vreq); - if (selectables.isEmpty()) { + //get locales present in list of literals + public List> getLocales(VitroRequest vreq, + List existingLiterals) { + Set locales = new HashSet(); + for(Literal literal : existingLiterals) { + String language = literal.getLanguage(); + if(!StringUtils.isEmpty(language)) { + locales.add(LanguageFilteringUtils.languageToLocale(language)); + } + } + if (locales.isEmpty()) { return Collections.emptyList(); } List> list = new ArrayList>(); Locale currentLocale = SelectedLocale.getCurrentLocale(vreq); - for (Locale locale : selectables) { + for (Locale locale : locales) { try { list.add(buildLocaleMap(locale, currentLocale)); } catch (FileNotFoundException e) { - log.warn("Can't show the Locale selector for '" + locale - + "': " + e); + log.warn("Can't show locale '" + locale + "': " + e); } } @@ -188,8 +196,8 @@ public class ViewLabelsServlet extends FreemarkerHttpServlet{ ArrayList labels = new ArrayList(); try { - //We want to get the labels for all the languages, not just the display language - ResultSet results = QueryUtils.getLanguageNeutralQueryResults(queryStr, vreq); + // Show only labels with current language filtering + ResultSet results = QueryUtils.getQueryResults(queryStr, vreq); while (results.hasNext()) { QuerySolution soln = results.nextSolution(); Literal nodeLiteral = soln.get("label").asLiteral(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java index c89119f5a..0f7154c88 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java @@ -6,14 +6,13 @@ import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; -import org.apache.jena.rdf.model.RDFNode; -import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModelBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; @@ -36,6 +35,7 @@ import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.ExecuteDataRetrieval; import edu.cornell.mannlib.vitro.webapp.web.beanswrappers.ReadOnlyBeansWrapper; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModel; +import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModelBuilder; import edu.ucsf.vitro.opensocial.OpenSocialManager; import freemarker.ext.beans.BeansWrapper; import freemarker.template.TemplateModel; @@ -123,8 +123,10 @@ class IndividualResponseBuilder { * into the data model: no real data can be modified. */ // body.put("individual", wrap(itm, BeansWrapper.EXPOSE_SAFE)); - body.put("labelCount", getLabelCount(itm.getUri(), vreq)); - body.put("languageCount", getLanguagesRepresentedCount(itm.getUri(), vreq)); + LabelAndLanguageCount labelAndLanguageCount = getLabelAndLanguageCount( + itm.getUri(), vreq); + body.put("labelCount", labelAndLanguageCount.getLabelCount()); + body.put("languageCount", labelAndLanguageCount.getLanguageCount()); //We also need to know the number of available locales body.put("localesCount", SelectedLocale.getSelectableLocales(vreq).size()); body.put("profileType", getProfileType(itm.getUri(), vreq)); @@ -282,61 +284,103 @@ class IndividualResponseBuilder { return map; } - private static String LABEL_COUNT_QUERY = "" - + "PREFIX rdfs: \n" - + "SELECT ( str(COUNT(?label)) AS ?labelCount ) WHERE { \n" - + " ?subject rdfs:label ?label \n" - + " FILTER isLiteral(?label) \n" - + "}" ; - - private static String DISTINCT_LANGUAGE_QUERY = "" + private static String LABEL_QUERY = "" + "PREFIX rdfs: \n" - + "SELECT ( str(COUNT(DISTINCT lang(?label))) AS ?languageCount ) WHERE { \n" + + "SELECT ?label WHERE { \n" + " ?subject rdfs:label ?label \n" + " FILTER isLiteral(?label) \n" + "}" ; + +// Queries that were previously used for counts via RDFService that didn't +// filter results by language. With language filtering, aggregate +// functions like COUNT() cannot be used. + +// private static String LABEL_COUNT_QUERY = "" +// + "PREFIX rdfs: \n" +// + "SELECT ( str(COUNT(?label)) AS ?labelCount ) WHERE { \n" +// + " ?subject rdfs:label ?label \n" +// + " FILTER isLiteral(?label) \n" +// + "}" ; - private static Integer getLabelCount(String subjectUri, VitroRequest vreq) { - String queryStr = QueryUtils.subUriForQueryVar(LABEL_COUNT_QUERY, "subject", subjectUri); +// private static String DISTINCT_LANGUAGE_QUERY = "" +// + "PREFIX rdfs: \n" +// + "SELECT ( str(COUNT(DISTINCT lang(?label))) AS ?languageCount ) WHERE { \n" +// + " ?subject rdfs:label ?label \n" +// + " FILTER isLiteral(?label) \n" +// + "}" ; + + private static LabelAndLanguageCount getLabelAndLanguageCount( + String subjectUri, VitroRequest vreq) { + // 1.12.0 Now filtering to only the labels for the current locale so as + // to be consistent with other editing forms. Because the language + // filter can only act on a result set containing actual literals, + // we can't do the counting with a COUNT() in the query itself. So + // we will now use the LABEL_QUERY instead of LABEL_COUNT_QUERY and + // count the rows and the number of distinct languages represented. + Set distinctLanguages = new HashSet(); + String queryStr = QueryUtils.subUriForQueryVar(LABEL_QUERY, "subject", subjectUri); log.debug("queryStr = " + queryStr); - int theCount = 0; - try { - //ResultSet results = QueryUtils.getQueryResults(queryStr, vreq); - //Get query results across all languages in order for template to show manage labels link correctly - ResultSet results = QueryUtils.getLanguageNeutralQueryResults(queryStr, vreq); - if (results.hasNext()) { - QuerySolution soln = results.nextSolution(); - RDFNode labelCount = soln.get("labelCount"); - if (labelCount != null && labelCount.isLiteral()) { - theCount = labelCount.asLiteral().getInt(); + int labelCount = 0; + try { + ResultSet results = QueryUtils.getQueryResults(queryStr, vreq); + while(results.hasNext()) { + QuerySolution qsoln = results.next(); + labelCount++; + String lang = qsoln.getLiteral("label").getLanguage(); + if(lang == null) { + lang = ""; } + distinctLanguages.add(lang); } } catch (Exception e) { log.error(e, e); } - return theCount; + return new LabelAndLanguageCount(labelCount, distinctLanguages.size()); + } + + private static class LabelAndLanguageCount { + + private Integer labelCount; + private Integer languageCount; + + public LabelAndLanguageCount(Integer labelCount, Integer languageCount) { + this.labelCount = labelCount; + this.languageCount = languageCount; + } + + public Integer getLabelCount() { + return this.labelCount; + } + + public Integer getLanguageCount() { + return this.languageCount; + } + } //what is the number of languages represented across the labels - private static Integer getLanguagesRepresentedCount(String subjectUri, VitroRequest vreq) { - String queryStr = QueryUtils.subUriForQueryVar(DISTINCT_LANGUAGE_QUERY, "subject", subjectUri); - log.debug("queryStr = " + queryStr); - int theCount = 0; - try { - - ResultSet results = QueryUtils.getLanguageNeutralQueryResults(queryStr, vreq); - if (results.hasNext()) { - QuerySolution soln = results.nextSolution(); - RDFNode languageCount = soln.get("languageCount"); - if (languageCount != null && languageCount.isLiteral()) { - theCount = languageCount.asLiteral().getInt(); - } - } - } catch (Exception e) { - log.error(e, e); - } - return theCount; - } + // This version not compatible with language-filtering RDF services +// private static Integer getLanguagesRepresentedCount(String subjectUri, VitroRequest vreq) { +// String queryStr = QueryUtils.subUriForQueryVar(DISTINCT_LANGUAGE_QUERY, "subject", subjectUri); +// log.debug("queryStr = " + queryStr); +// int theCount = 0; +// try { +// +// ResultSet results = QueryUtils.getLanguageNeutralQueryResults(queryStr, vreq); +// if (results.hasNext()) { +// QuerySolution soln = results.nextSolution(); +// RDFNode languageCount = soln.get("languageCount"); +// if (languageCount != null && languageCount.isLiteral()) { +// theCount = languageCount.asLiteral().getInt(); +// log.info("Language count is " + theCount); +// } +// } +// } catch (Exception e) { +// log.error(e, e); +// } +// log.info("Returning language count " + theCount); +// return theCount; +// } private static String PROFILE_TYPE_QUERY = "" + "PREFIX display: \n" diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/GetRandomSearchIndividualsByVClass.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/GetRandomSearchIndividualsByVClass.java index 8f3aaa311..325683d54 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/GetRandomSearchIndividualsByVClass.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/GetRandomSearchIndividualsByVClass.java @@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.controller.json; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -15,6 +16,7 @@ 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.dao.IndividualDao; +import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService; import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext; import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewServiceSetup; @@ -73,7 +75,11 @@ public class GetRandomSearchIndividualsByVClass extends GetSearchIndividualsByVC modelMap.put("individual", IndividualTemplateModelBuilder.build(individual, vreq)); modelMap.put("vclass", vclassName); - + String langCtx = vreq.getLocale().getLanguage(); //UQAM-Linguistic-Management build the linguistic context + if (!vreq.getLocale().getCountry().isEmpty()) { + langCtx += "-" + vreq.getLocale().getCountry(); + } + modelMap.put("langCtx", langCtx); // UQAM-Linguistic-Management add the linguistic context to map ShortViewService svs = ShortViewServiceSetup.getService(ctx); return svs.renderShortView(individual, ShortViewContext.BROWSE, modelMap, vreq); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java index fdd9387c0..fba900cad 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java @@ -155,6 +155,7 @@ public class VitroVocabulary { public static final String USERACCOUNT_LAST_LOGIN_TIME = VITRO_AUTH + "lastLoginTime"; public static final String USERACCOUNT_STATUS = VITRO_AUTH + "status"; public static final String USERACCOUNT_PASSWORD_LINK_EXPIRES = VITRO_AUTH + "passwordLinkExpires"; + public static final String USERACCOUNT_EMAIL_KEY = VITRO_AUTH + "emailKey"; public static final String USERACCOUNT_PASSWORD_CHANGE_REQUIRED = VITRO_AUTH + "passwordChangeRequired"; public static final String USERACCOUNT_EXTERNAL_AUTH_ID = VITRO_AUTH + "externalAuthId"; public static final String USERACCOUNT_EXTERNAL_AUTH_ONLY = VITRO_AUTH + "externalAuthOnly"; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactory.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactory.java index 622367e23..7ebe8a209 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactory.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactory.java @@ -5,6 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.dao; import java.util.List; import java.util.Set; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; + public interface WebappDaoFactory { /** @@ -132,4 +134,6 @@ public interface WebappDaoFactory { public MenuDao getMenuDao(); + public I18nBundle getI18nBundle(); + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryConfig.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryConfig.java index 3d3dc4afd..9ab5563f5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryConfig.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryConfig.java @@ -5,21 +5,25 @@ package edu.cornell.mannlib.vitro.webapp.dao; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import edu.cornell.mannlib.vitro.webapp.dao.PropertyDao.FullPropertyKey; +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; public class WebappDaoFactoryConfig { private List preferredLanguages; + private List preferredLocales; private String defaultNamespace; private Set nonUserNamespaces; private boolean isUnderlyingStoreReasoned = false; public Map customListViewConfigFileMap; public WebappDaoFactoryConfig() { - preferredLanguages = Arrays.asList("en-US", "en", "EN"); + preferredLanguages = Arrays.asList("en-US", "en", "EN"); + preferredLocales = LanguageFilteringUtils.languagesToLocales(preferredLanguages); defaultNamespace = "http://vitro.mannlib.cornell.edu/ns/default#"; nonUserNamespaces = new HashSet(); nonUserNamespaces.add(VitroVocabulary.vitroURI); @@ -33,6 +37,14 @@ public class WebappDaoFactoryConfig { this.preferredLanguages = pl; } + public List getPreferredLocales() { + return this.preferredLocales; + } + + public void setPreferredLocales(List pl) { + this.preferredLocales = pl; + } + public String getDefaultNamespace() { return defaultNamespace; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java index 85d21a14f..9028f3c93 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java @@ -17,7 +17,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; -class DataPropertyDaoFiltering extends BaseFiltering implements DataPropertyDao{ +public class DataPropertyDaoFiltering extends BaseFiltering implements DataPropertyDao{ final DataPropertyDao innerDataPropertyDao; final VitroFilters filters; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java index 5bcc78830..961763297 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java @@ -18,7 +18,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; -class DataPropertyStatementDaoFiltering extends BaseFiltering implements DataPropertyStatementDao{ +public class DataPropertyStatementDaoFiltering extends BaseFiltering implements DataPropertyStatementDao{ final DataPropertyStatementDao innerDataPropertyStatementDao; final VitroFilters filters; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java index bc79e1443..796bbc8df 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java @@ -26,47 +26,34 @@ public class FauxPropertyDaoFiltering extends BaseFiltering implements FauxPrope @Override public List getFauxPropertiesForBaseUri(String uri) { - // TODO Auto-generated method stub - throw new RuntimeException( - "FauxPropertyDao.getFauxPropertiesForBaseUri() not implemented."); + return innerFauxPropertyDao.getFauxPropertiesForBaseUri(uri); } @Override public FauxProperty getFauxPropertyFromContextUri(String contextUri) { - // TODO Auto-generated method stub - throw new RuntimeException( - "FauxPropertyDaoFiltering.getFauxPropertyFromConfigContextUri() not implemented."); - + return innerFauxPropertyDao.getFauxPropertyFromContextUri(contextUri); } @Override public FauxProperty getFauxPropertyByUris(String domainUri, String baseUri, String rangeUri) { - // TODO Auto-generated method stub - throw new RuntimeException( - "FauxPropertyDaoFiltering.getFauxPropertyByUris() not implemented."); - + return innerFauxPropertyDao.getFauxPropertyByUris(domainUri, baseUri, + rangeUri); } @Override public void updateFauxProperty(FauxProperty fp) { - // TODO Auto-generated method stub - throw new RuntimeException("FauxPropertyDaoFiltering.updateFauxProperty() not implemented."); - + innerFauxPropertyDao.updateFauxProperty(fp); } @Override public void deleteFauxProperty(FauxProperty fp) { - // TODO Auto-generated method stub - throw new RuntimeException("FauxPropertyDao.deleteFauxProperty() not implemented."); - + innerFauxPropertyDao.deleteFauxProperty(fp); } @Override public void insertFauxProperty(FauxProperty fp) { - // TODO Auto-generated method stub - throw new RuntimeException("FauxPropertyDao.insertFauxProperty() not implemented."); - + innerFauxPropertyDao.insertFauxProperty(fp); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java index 74a275f8b..e9121cd7f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java @@ -20,7 +20,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; -class IndividualDaoFiltering extends BaseFiltering implements IndividualDao{ +public class IndividualDaoFiltering extends BaseFiltering implements IndividualDao{ IndividualDao innerIndividualDao; VitroFilters filters; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyDaoFiltering.java index c85ca36a5..30cd70521 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyDaoFiltering.java @@ -17,7 +17,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; -class ObjectPropertyDaoFiltering extends BaseFiltering implements ObjectPropertyDao{ +public class ObjectPropertyDaoFiltering extends BaseFiltering implements ObjectPropertyDao{ final ObjectPropertyDao innerObjectPropertyDao; final VitroFilters filters; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyStatementDaoFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyStatementDaoFiltering.java index 3aa4ea3ab..8ccc636cc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyStatementDaoFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/ObjectPropertyStatementDaoFiltering.java @@ -16,7 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl; import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; -class ObjectPropertyStatementDaoFiltering extends BaseFiltering implements ObjectPropertyStatementDao{ +public class ObjectPropertyStatementDaoFiltering extends BaseFiltering implements ObjectPropertyStatementDao{ final ObjectPropertyStatementDao innerObjectPropertyStatementDao; final VitroFilters filters; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/WebappDaoFactoryFiltering.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/WebappDaoFactoryFiltering.java index 84e2823d7..fdb386a12 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/WebappDaoFactoryFiltering.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/WebappDaoFactoryFiltering.java @@ -24,6 +24,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; /** * This wraps a WebappDaoFactory and applies filtering. @@ -67,7 +68,7 @@ public class WebappDaoFactoryFiltering implements WebappDaoFactory { transient private PropertyGroupDao filteringPropertyGroupDao=null; transient private PropertyInstanceDao filteringPropertyInstanceDao=null; - public WebappDaoFactoryFiltering( WebappDaoFactory innerDao, VitroFilters filters){ + public WebappDaoFactoryFiltering(WebappDaoFactory innerDao, VitroFilters filters){ if( innerDao == null ) throw new Error("innerWebappDaoFactory must be non-null"); this.filters = filters; @@ -276,4 +277,9 @@ public class WebappDaoFactoryFiltering implements WebappDaoFactory { public void close() { innerWebappDaoFactory.close(); } + + @Override + public I18nBundle getI18nBundle() { + return innerWebappDaoFactory.getI18nBundle(); + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ApplicationDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ApplicationDaoJena.java index 884c5f964..9e1898020 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ApplicationDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ApplicationDaoJena.java @@ -83,22 +83,21 @@ public class ApplicationDaoJena extends JenaBaseDao implements ApplicationDao { } ontModel.enterCriticalSection(Lock.WRITE); try { - appInd.setLabel(application.getApplicationName(), null); - updatePropertyStringValue( - appInd, APPLICATION_ABOUTTEXT, application.getAboutText(), - ontModel); - updatePropertyStringValue( + updateRDFSLabel(appInd, application.getApplicationName()); + updatePlainLiteralValue( + appInd, APPLICATION_ABOUTTEXT, application.getAboutText()); + updatePlainLiteralValue( appInd, APPLICATION_ACKNOWLEGETEXT, - application.getAcknowledgeText(), ontModel); + application.getAcknowledgeText()); updatePropertyStringValue( appInd, APPLICATION_CONTACTMAIL, application.getContactMail(), ontModel); updatePropertyStringValue( appInd, APPLICATION_CORRECTIONMAIL, application.getCorrectionMail(), ontModel); - updatePropertyStringValue( + updatePlainLiteralValue( appInd, APPLICATION_COPYRIGHTANCHOR, - application.getCopyrightAnchor(), ontModel); + application.getCopyrightAnchor()); updatePropertyStringValue( appInd, APPLICATION_COPYRIGHTURL, application.getCopyrightURL(), ontModel); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java index a17ef6ec1..5c836281d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java @@ -516,9 +516,9 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements if (dtp.getFunctional()) { ontModel.add(jDataprop,RDF.type,OWL.FunctionalProperty); } - addPropertyStringValue(jDataprop, EXAMPLE, dtp.getExample(), ontModel); - addPropertyStringValue(jDataprop, DESCRIPTION_ANNOT, dtp.getDescription(), ontModel); - addPropertyStringValue(jDataprop, PUBLIC_DESCRIPTION_ANNOT, dtp.getPublicDescription(), ontModel); + updatePlainLiteralValue(jDataprop, EXAMPLE, dtp.getExample()); + updatePlainLiteralValue(jDataprop, DESCRIPTION_ANNOT, dtp.getDescription()); + updatePlainLiteralValue(jDataprop, PUBLIC_DESCRIPTION_ANNOT, dtp.getPublicDescription()); addPropertyStringValue(jDataprop, EDITING, dtp.getEditing(), ontModel); addPropertyNonNegativeIntValue(jDataprop, DISPLAY_RANK_ANNOT, dtp.getDisplayTier(), ontModel); addPropertyNonNegativeIntValue(jDataprop, DISPLAY_LIMIT, dtp.getDisplayLimit(), ontModel); @@ -587,9 +587,9 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements } } - updatePropertyStringValue(jDataprop, EXAMPLE, dtp.getExample(), ontModel); - updatePropertyStringValue(jDataprop, DESCRIPTION_ANNOT, dtp.getDescription(), ontModel); - updatePropertyStringValue(jDataprop, PUBLIC_DESCRIPTION_ANNOT, dtp.getPublicDescription(), ontModel); + updatePlainLiteralValue(jDataprop, EXAMPLE, dtp.getExample()); + updatePlainLiteralValue(jDataprop, DESCRIPTION_ANNOT, dtp.getDescription()); + updatePlainLiteralValue(jDataprop, PUBLIC_DESCRIPTION_ANNOT, dtp.getPublicDescription()); updatePropertyStringValue(jDataprop, EDITING, dtp.getEditing(), ontModel); updatePropertyNonNegativeIntValue(jDataprop, DISPLAY_RANK_ANNOT, dtp.getDisplayTier(), ontModel); updatePropertyNonNegativeIntValue(jDataprop, DISPLAY_LIMIT, dtp.getDisplayLimit(), ontModel); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java index 744881f7f..a454e1d1e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java @@ -230,10 +230,9 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao OBJECT_PROPERTY_DISPLAY_CONFIG); addPropertyResourceURINotEmpty(config, PROPERTY_GROUP, fp.getGroupURI()); - addPropertyStringValue(config, DISPLAY_NAME, fp.getDisplayName(), - displayModel); - addPropertyStringValue(config, PUBLIC_DESCRIPTION_ANNOT, - fp.getPublicDescription(), displayModel); + updatePlainLiteralValue(config, DISPLAY_NAME, fp.getDisplayName()); + updatePlainLiteralValue(config, PUBLIC_DESCRIPTION_ANNOT, + fp.getPublicDescription()); addPropertyIntValue(config, DISPLAY_RANK_ANNOT, fp.getDisplayTier(), displayModel); addPropertyIntValue(config, DISPLAY_LIMIT, fp.getDisplayLimit(), @@ -328,10 +327,10 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao .getConfigUri()); updatePropertyResourceURIValue(config, PROPERTY_GROUP, fp.getGroupURI()); - updatePropertyStringValue(config, DISPLAY_NAME, - fp.getDisplayName(), displayModel); - updatePropertyStringValue(config, PUBLIC_DESCRIPTION_ANNOT, - fp.getPublicDescription(), displayModel); + updatePlainLiteralValue(config, DISPLAY_NAME, + fp.getDisplayName()); + updatePlainLiteralValue(config, PUBLIC_DESCRIPTION_ANNOT, + fp.getPublicDescription()); updatePropertyIntValue(config, DISPLAY_RANK_ANNOT, fp.getDisplayTier(), displayModel); updatePropertyIntValue(config, DISPLAY_LIMIT, fp.getDisplayLimit(), diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java index f96349bcf..9b0458109 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java @@ -5,8 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; @@ -14,14 +14,14 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.jena.iri.IRI; -import org.apache.jena.iri.IRIFactory; - import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; +import org.apache.jena.iri.IRI; +import org.apache.jena.iri.IRIFactory; import org.apache.jena.ontology.DatatypeProperty; import org.apache.jena.ontology.ObjectProperty; import org.apache.jena.ontology.OntClass; @@ -39,6 +39,7 @@ import org.apache.jena.rdf.model.NodeIterator; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.shared.Lock; @@ -752,22 +753,111 @@ public class JenaBaseDao extends JenaBaseDaoCon { } } } + + /** + * Add to an OntResource an rdfs:label value with lexical form and default + * language tag. Remove any other existing values in default language. + * If lexicalForm parameter is null, remove all plain literal values of + * Property in default language. + * @param ontRes may not be null + * @param lexicalForm may be null. If null, existing values will be deleted + * but none will be added. + */ + protected void updateRDFSLabel(OntResource ontRes, String lexicalForm) { + updatePlainLiteralValue(ontRes, RDFS.label, lexicalForm); + } + + /** + * Add to an OntResource an rdfs:label value with lexical form and optional + * language tag. Remove any other existing plain literal values that match + * specified language or lack language tags if no language is supplied. + * If lexicalForm parameter is null, remove all plain literal labels in + * specified language, or all existing language-less labels + * if no language is specified. + * @param ontRes may not be null + * @param lexicalForm may be null. If null, existing values will be deleted + * but none will be added. + * @param lang may be null. If null, method acts on language-less plain + * literal labels and ignores those with language tags. + */ + protected void updateRDFSLabel(OntResource ontRes, String lexicalForm, String lang) { + updatePlainLiteralValue(ontRes, RDFS.label, lexicalForm, lang); + } /** - * convenience method for updating the RDFS label + * Add to an OntResource a Property value with lexical form and default + * language tag. Remove any other existing values in default language. + * If lexicalForm parameter is null, remove all plain literal values of + * Property in default language. + * @param ontRes may not be null + * @param lexicalForm may be null. If null, existing values will be deleted + * but none will be added. */ - protected void updateRDFSLabel(OntResource ontRes, String label) { - - if (label != null && label.length() > 0) { - - String existingValue = ontRes.getLabel(getDefaultLanguage()); - - if (existingValue == null || !existingValue.equals(label)) { - ontRes.setLabel(label, getDefaultLanguage()); - } - } else { - ontRes.removeAll(RDFS.label); - } + protected void updatePlainLiteralValue(OntResource ontRes, Property property, + String lexicalForm) { + updatePlainLiteralValue(ontRes, property, lexicalForm, getDefaultLanguage()); + } + + /** + * Add to an OntResource a Property value with lexical form and optional + * language tag. Remove any other existing plain literal values that match + * specified language or lack language tags if no language is supplied. + * If lexicalForm parameter is null, remove all plain literal values of + * Property in specified language, or all existing language-less literals + * if no language is specified. + * @param ontRes may not be null + * @param lexicalForm may be null. If null, existing values will be deleted + * but none will be added. + * @param lang may be null. If null, method acts on language-less + * plain literal values and ignores those with language tags. + */ + protected void updatePlainLiteralValue(OntResource ontRes, Property property, + String lexicalForm, String lang) { + if(ontRes == null) { + throw new IllegalArgumentException("ontRes may not be null."); + } + boolean addNew = true; + List toRemove = new ArrayList(); + StmtIterator existingStmts = ontRes.listProperties(property); + while(existingStmts.hasNext()) { + Statement stmt = existingStmts.next(); + if(stmt.getObject().isLiteral()) { + Literal lit = stmt.getObject().asLiteral(); + if( (lang == null && isLanguageLessPlainLiteral(lit)) + || (lang != null && lang.equals(lit.getLanguage())) ) { + if(!lit.getLexicalForm().equals(lexicalForm)) { + toRemove.add(stmt); + } else { + // New literal already exists in the model. + // Do not add it again. + addNew = false; + } + } + } + } + if(!toRemove.isEmpty()) { + ontRes.getModel().remove(toRemove); + } + if (addNew && (lexicalForm != null)) { + if(!StringUtils.isEmpty(lang)) { + ontRes.addProperty(property, ResourceFactory.createLangLiteral( + lexicalForm, lang)); + } else { + ontRes.addProperty(property, ResourceFactory.createPlainLiteral( + lexicalForm)); + } + } + } + + private boolean isLanguageLessPlainLiteral(Literal lit) { + // In RDF 1.1 all the language-less literals get datatype xsd:string. + // The null datatype check is here just in case this gets run on an older + // version of Jena. rdf:PlainLiteral is also a datatype, but doesn't + // (yet) seem to be used by Jena. + return StringUtils.isEmpty(lit.getLanguage()) + && ((lit.getDatatype() == null) + || XSDDatatype.XSDstring.equals(lit.getDatatype()) || + (RDF.getURI() + "PlainLiteral").equals(lit.getDatatypeURI())); } private Literal getLabel(String lang, ListlabelList) { @@ -780,6 +870,13 @@ public class JenaBaseDao extends JenaBaseDaoCon { } if ((lang != null) && (lang.equals(labelLanguage))) { return labelLit; + } else + /* + * UQAM-Linguistic-Management + * Check for country-part of lang (ex: 'en' for default consideration of labelLanguage in english but not encoded by 'en-US' most case of labels in vivo.owl) + */ + if ((lang != null) && (Arrays.asList(lang.split("-")).get(0).equals(labelLanguage))) { + return labelLit; } } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoCon.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoCon.java index d618ac804..71fd6447a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoCon.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoCon.java @@ -121,6 +121,7 @@ public class JenaBaseDaoCon { protected DatatypeProperty USERACCOUNT_LAST_LOGIN_TIME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_LAST_LOGIN_TIME); protected DatatypeProperty USERACCOUNT_STATUS = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_STATUS); protected DatatypeProperty USERACCOUNT_PASSWORD_LINK_EXPIRES = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_PASSWORD_LINK_EXPIRES); + protected DatatypeProperty USERACCOUNT_EMAIL_KEY = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EMAIL_KEY); protected DatatypeProperty USERACCOUNT_PASSWORD_CHANGE_REQUIRED = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_PASSWORD_CHANGE_REQUIRED); protected DatatypeProperty USERACCOUNT_EXTERNAL_AUTH_ID = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EXTERNAL_AUTH_ID); protected DatatypeProperty USERACCOUNT_EXTERNAL_AUTH_ONLY = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EXTERNAL_AUTH_ONLY); 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 73da8f76b..30dd022c9 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 @@ -72,6 +72,7 @@ public class MenuDaoJena extends JenaBaseDao implements MenuDao { return getMenu( getOntModelSelector().getDisplayModel(), url ); } + @Override public MainMenu getMainMenu( ServletRequest req, String url ) { OntModel displayModel = LanguageFilteringUtils.wrapOntModelInALanguageFilter(getOntModelSelector().getDisplayModel(), req ); return getMenu(displayModel, url) ; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java index ea4d5f041..9ae00d59e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java @@ -714,14 +714,14 @@ public class ObjectPropertyDaoJena extends PropertyDaoJena implements ObjectProp } } - updatePropertyStringValue(p,EXAMPLE_ANNOT,prop.getExample(),getOntModel()); - updatePropertyStringValue(p,DESCRIPTION_ANNOT,prop.getDescription(),getOntModel()); - updatePropertyStringValue(p,PUBLIC_DESCRIPTION_ANNOT,prop.getPublicDescription(),getOntModel()); + updatePlainLiteralValue(p, EXAMPLE_ANNOT, prop.getExample()); + updatePlainLiteralValue(p, DESCRIPTION_ANNOT, prop.getDescription()); + updatePlainLiteralValue(p, PUBLIC_DESCRIPTION_ANNOT, prop.getPublicDescription()); updatePropertyNonNegativeIntegerValue(p,DISPLAY_LIMIT,prop.getDomainDisplayLimitInteger(),getOntModel()); updatePropertyStringValue(p,PROPERTY_ENTITYSORTDIRECTION,prop.getDomainEntitySortDirection(),getOntModel()); if (inv != null) { - updatePropertyStringValue(inv,EXAMPLE_ANNOT,prop.getExample(),getOntModel()); - updatePropertyStringValue(inv,DESCRIPTION_ANNOT,prop.getDescription(),getOntModel()); + updatePlainLiteralValue(inv, EXAMPLE_ANNOT, prop.getExample()); + updatePlainLiteralValue(inv, DESCRIPTION_ANNOT, prop.getDescription()); updatePropertyNonNegativeIntegerValue(inv,DISPLAY_LIMIT,prop.getRangeDisplayLimitInteger(),getOntModel()); updatePropertyStringValue(inv,PROPERTY_ENTITYSORTDIRECTION,prop.getRangeEntitySortDirection(),getOntModel()); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java index 491858453..f6c3b4dec 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java @@ -12,10 +12,10 @@ import java.util.function.Supplier; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.graph.Capabilities; import org.apache.jena.graph.Graph; import org.apache.jena.graph.GraphEventManager; +import org.apache.jena.graph.GraphListener; import org.apache.jena.graph.GraphStatisticsHandler; import org.apache.jena.graph.Node; import org.apache.jena.graph.TransactionHandler; @@ -23,7 +23,6 @@ import org.apache.jena.graph.Triple; import org.apache.jena.graph.impl.GraphWithPerform; import org.apache.jena.graph.impl.SimpleEventManager; import org.apache.jena.query.QuerySolution; -import org.apache.jena.rdf.listeners.StatementListener; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.StmtIterator; @@ -290,10 +289,15 @@ public class RDFServiceGraph implements GraphWithPerform { literalBuff.append("\""); pyString(literalBuff, node.getLiteralLexicalForm()); literalBuff.append("\""); - if (node.getLiteralDatatypeURI() != null) { - literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); - } else if (!StringUtils.isEmpty(node.getLiteralLanguage())) { + /* + * UQAM-Bug-Correction + * reversing the condition tests. + * It is important to prioritize the language typology test in order to exploit the linguistic context in testing the type of data + */ + if (!StringUtils.isEmpty(node.getLiteralLanguage())) { literalBuff.append("@").append(node.getLiteralLanguage()); + } else if (node.getLiteralDatatypeURI() != null) { + literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); } return literalBuff.toString(); } else { @@ -404,7 +408,18 @@ public class RDFServiceGraph implements GraphWithPerform { @Override public GraphEventManager getEventManager() { if (eventManager == null) { - eventManager = new SimpleEventManager(this); + eventManager = new SimpleEventManager() { + @Override + public void notifyEvent(Graph g, Object event) { + ChangeSet changeSet = rdfService.manufactureChangeSet(); + changeSet.addPreChangeEvent(event); + try { + rdfService.changeSetUpdate(changeSet); + } catch (RDFServiceException e) { + throw new RuntimeException(e); + } + } + }; } return eventManager; } @@ -590,21 +605,7 @@ public class RDFServiceGraph implements GraphWithPerform { } public static Model createRDFServiceModel(final RDFServiceGraph g) { - Model m = VitroModelFactory.createModelForGraph(g); - m.register(new StatementListener() { - @Override - public void notifyEvent(Model m, Object event) { - ChangeSet changeSet = g.getRDFService().manufactureChangeSet(); - changeSet.addPreChangeEvent(event); - try { - g.getRDFService().changeSetUpdate(changeSet); - } catch (RDFServiceException e) { - throw new RuntimeException(e); - } - } - - }); - return m; + return VitroModelFactory.createModelForGraph(g); } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraph.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraph.java index 8b2ed8668..f0e55e239 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraph.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraph.java @@ -232,10 +232,10 @@ public class SparqlGraph implements GraphWithPerform { literalBuff.append("\""); pyString(literalBuff, node.getLiteralLexicalForm()); literalBuff.append("\""); - if (node.getLiteralDatatypeURI() != null) { - literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); - } else if (!StringUtils.isEmpty(node.getLiteralLanguage())) { + if (!StringUtils.isEmpty(node.getLiteralLanguage())) { literalBuff.append("@").append(node.getLiteralLanguage()); + } else if (node.getLiteralDatatypeURI() != null) { + literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); } return literalBuff.toString(); } else { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java index 73557fd5c..29c1d5a29 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java @@ -4,7 +4,6 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Random; @@ -98,6 +97,8 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao u.setOldPassword(getPropertyStringValue(r, USERACCOUNT_OLD_PASSWORD)); u.setPasswordLinkExpires(getPropertyLongValue(r, USERACCOUNT_PASSWORD_LINK_EXPIRES)); + u.setEmailKey(getPropertyStringValue(r,USERACCOUNT_EMAIL_KEY)); + u.setPasswordChangeRequired(getPropertyBooleanValue(r, USERACCOUNT_PASSWORD_CHANGE_REQUIRED)); u.setExternalAuthOnly(getPropertyBooleanValue(r, @@ -240,6 +241,8 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao userAccount.getLoginCount(), model); addPropertyLongValue(res, USERACCOUNT_LAST_LOGIN_TIME, userAccount.getLastLoginTime(), model); + addPropertyStringValue(res, USERACCOUNT_EMAIL_KEY, + userAccount.getEmailKey(), model); if (userAccount.getStatus() != null) { addPropertyStringValue(res, USERACCOUNT_STATUS, userAccount .getStatus().toString(), model); @@ -306,6 +309,8 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao userAccount.getLoginCount(), model); updatePropertyLongValue(res, USERACCOUNT_LAST_LOGIN_TIME, userAccount.getLastLoginTime(), model); + updatePropertyStringValue(res, USERACCOUNT_EMAIL_KEY, + userAccount.getEmailKey(), model); if (userAccount.getStatus() == null) { updatePropertyStringValue(res, USERACCOUNT_STATUS, null, model); } else { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoJena.java index 8d79155b1..0c3183f50 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoJena.java @@ -2,6 +2,8 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import static java.lang.String.format; + import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -60,15 +62,18 @@ import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import edu.cornell.mannlib.vitro.webapp.web.URLEncoder; public class VClassDaoJena extends JenaBaseDao implements VClassDao { protected static final Log log = LogFactory.getLog(VClassDaoJena.class); + private final I18nBundle i18n; private boolean isUnderlyingStoreReasoned = false; public VClassDaoJena(WebappDaoFactoryJena wadf, boolean isUnderlyingStoreReasoned) { super(wadf); + this.i18n = wadf.getI18nBundle(); this.isUnderlyingStoreReasoned = isUnderlyingStoreReasoned; } @@ -91,17 +96,19 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { Restriction rest = cls.asRestriction(); OntProperty onProperty = rest.getOnProperty(); StringBuilder labelStr = new StringBuilder(); - labelStr.append("restriction on ").append(getLabelOrId(onProperty)).append(": "); + labelStr.append(format("%s ", i18n.text("restriction_on"))) + .append(getLabelOrId(onProperty)) + .append(": "); if (rest.isAllValuesFromRestriction() || rest.isSomeValuesFromRestriction()) { Resource fillerRes = null; if (rest.isAllValuesFromRestriction()) { AllValuesFromRestriction avfRest = rest.asAllValuesFromRestriction(); fillerRes = avfRest.getAllValuesFrom(); - labelStr.append("all values from "); + labelStr.append(format("%s ", i18n.text("all_values_from"))); } else { SomeValuesFromRestriction svfRest = rest.asSomeValuesFromRestriction(); fillerRes = svfRest.getSomeValuesFrom(); - labelStr.append("some values from "); + labelStr.append(format("%s ", i18n.text("some_values_from"))); } if (fillerRes.canAs(OntClass.class)) { OntClass avf = fillerRes.as(OntClass.class); @@ -115,7 +122,7 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { } } else if (rest.isHasValueRestriction()) { HasValueRestriction hvRest = rest.asHasValueRestriction(); - labelStr.append("has value "); + labelStr.append(format("%s ", i18n.text("has_value"))); RDFNode fillerNode = hvRest.getHasValue(); try { if (fillerNode.isResource()) { @@ -128,22 +135,22 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { } } else if (rest.isMinCardinalityRestriction()) { MinCardinalityRestriction mcRest = rest.asMinCardinalityRestriction(); - labelStr.append("minimum cardinality "); + labelStr.append(format("%s ", i18n.text("minimum_cardinality"))); labelStr.append(mcRest.getMinCardinality()); } else if (rest.isMaxCardinalityRestriction()) { MaxCardinalityRestriction mcRest = rest.asMaxCardinalityRestriction(); - labelStr.append("maximum cardinality "); + labelStr.append(format("%s ", i18n.text("maximum_cardinality"))); labelStr.append(mcRest.getMaxCardinality()); } else if (rest.isCardinalityRestriction()) { CardinalityRestriction cRest = rest.asCardinalityRestriction(); - labelStr.append("cardinality "); + labelStr.append(format("%s ", i18n.text("cardinality"))); labelStr.append(cRest.getCardinality()); } return labelStr.toString(); } else if (isBooleanClassExpression(cls)) { StringBuilder labelStr = new StringBuilder("("); if (cls.isComplementClass()) { - labelStr.append("not "); + labelStr.append(format("%s ", i18n.text("not"))); ComplementClass ccls = cls.as(ComplementClass.class); labelStr.append(getLabelForClass(ccls.getOperand(), withPrefix, forPickList)); } else if (cls.isIntersectionClass()) { @@ -153,7 +160,7 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { OntClass operand = operandIt.next(); labelStr.append(getLabelForClass(operand, withPrefix, forPickList)); if (operandIt.hasNext()) { - labelStr.append(" and "); + labelStr.append(format(" %s ", i18n.text("and"))); } } } else if (cls.isUnionClass()) { @@ -163,7 +170,7 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { OntClass operand = operandIt.next(); labelStr.append(getLabelForClass(operand, withPrefix, forPickList)); if (operandIt.hasNext()) { - labelStr.append(" or "); + labelStr.append(format(" %s ", i18n.text("or"))); } } } @@ -952,9 +959,9 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { } catch (Exception e) { log.error("error linking class "+cls.getURI()+" to class group"); } - addPropertyStringValue(ontCls,SHORTDEF,cls.getShortDef(),ontModel); - addPropertyStringValue(ontCls,EXAMPLE_ANNOT,cls.getExample(),ontModel); - addPropertyStringValue(ontCls,DESCRIPTION_ANNOT,cls.getDescription(),ontModel); + updatePlainLiteralValue(ontCls, SHORTDEF, cls.getShortDef()); + updatePlainLiteralValue(ontCls, EXAMPLE_ANNOT, cls.getExample()); + updatePlainLiteralValue(ontCls, DESCRIPTION_ANNOT, cls.getDescription()); addPropertyIntValue(ontCls,DISPLAY_LIMIT,cls.getDisplayLimit(),ontModel); addPropertyIntValue(ontCls,DISPLAY_RANK_ANNOT,cls.getDisplayRank(),ontModel); @@ -1011,9 +1018,9 @@ public class VClassDaoJena extends JenaBaseDao implements VClassDao { if (ontCls != null) { updateRDFSLabel(ontCls, cls.getName()); updatePropertyResourceURIValue(ontCls,IN_CLASSGROUP,cls.getGroupURI(),ontModel); - updatePropertyStringValue(ontCls,SHORTDEF,cls.getShortDef(),ontModel); - updatePropertyStringValue(ontCls,EXAMPLE_ANNOT,cls.getExample(),ontModel); - updatePropertyStringValue(ontCls,DESCRIPTION_ANNOT,cls.getDescription(),ontModel); + updatePlainLiteralValue(ontCls, SHORTDEF, cls.getShortDef()); + updatePlainLiteralValue(ontCls, EXAMPLE_ANNOT, cls.getExample()); + updatePlainLiteralValue(ontCls, DESCRIPTION_ANNOT, cls.getDescription()); updatePropertyNonNegativeIntValue(ontCls,DISPLAY_LIMIT,cls.getDisplayLimit(),ontModel); updatePropertyNonNegativeIntValue(ontCls,DISPLAY_RANK_ANNOT,cls.getDisplayRank(),ontModel); updatePropertyFloatValue(ontCls, SEARCH_BOOST_ANNOT, cls.getSearchBoost(), ontModel); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupDaoJena.java index 3ba868de8..5a655660b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupDaoJena.java @@ -276,7 +276,7 @@ public class VClassGroupDaoJena extends JenaBaseDao implements VClassGroupDao { try { Individual groupInd = ontModel.getIndividual(vcg.getURI()); try { - groupInd.setLabel(vcg.getPublicName(), getDefaultLanguage()); + updateRDFSLabel(groupInd, vcg.getPublicName(), getDefaultLanguage()); } catch (Exception e) { log.error("error updating name for "+groupInd.getURI()); } 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 26aabf0be..813cf27ad 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 @@ -50,6 +50,8 @@ import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryConfig; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; @@ -611,5 +613,10 @@ public class WebappDaoFactoryJena implements WebappDaoFactory { } } + @Override + public I18nBundle getI18nBundle() { + // return context based bundle for preferred locales + return I18n.bundle(config.getPreferredLocales()); + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/EditLiteral.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/EditLiteral.java index 95f2848b7..8ff13bfc0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/EditLiteral.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/EditLiteral.java @@ -132,6 +132,11 @@ public class EditLiteral implements Literal { throw new UnsupportedOperationException(); } + @Override + public boolean isStmtResource() { + throw new UnsupportedOperationException(); + } + public Literal inModel(Model model) { throw new UnsupportedOperationException(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/listener/impl/IndividualDataPropertyStatementProcessor.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/listener/impl/IndividualDataPropertyStatementProcessor.java index f2b6cad4f..b2758c7fd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/listener/impl/IndividualDataPropertyStatementProcessor.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/listener/impl/IndividualDataPropertyStatementProcessor.java @@ -4,7 +4,6 @@ package edu.cornell.mannlib.vitro.webapp.edit.listener.impl; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import org.apache.commons.logging.Log; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwo.java index c5eb8280f..1e942aa8e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwo.java @@ -24,15 +24,25 @@ import edu.cornell.mannlib.vitro.webapp.beans.Datatype; import edu.cornell.mannlib.vitro.webapp.dao.jena.DatatypeDaoJena; import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; - +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; public class BasicValidationVTwo { + public final static String REQUIRED_FIELD_EMPTY_MSG = "required_field_empty_msg"; + + public final static String DATA_NOT_PAST_MSG = "data_not_past_msg"; + public final static String INVALID_DATE_FORM_MSG = "invalid_date_form_msg"; + public final static String FILE_MUST_BE_ENTERED_MSG = "file_must_be_entered_msg"; + public final static String INVALID_URL_MSG = "invalid_url_msg"; + + private I18nBundle i18n; + Map> varsToValidations; EditConfigurationVTwo editConfig; - public BasicValidationVTwo(EditConfigurationVTwo editConfig, MultiValueEditSubmission editSub){ + public BasicValidationVTwo(EditConfigurationVTwo editConfig, I18nBundle i18n){ this.editConfig = editConfig; + this.i18n = i18n; Map> validatorsForFields = new HashMap>(); for(String fieldName: editConfig.getFields().keySet()){ FieldVTwo field = editConfig.getField(fieldName); @@ -42,8 +52,9 @@ public class BasicValidationVTwo { checkValidations(); } - public BasicValidationVTwo(Map> varsToValidations){ + public BasicValidationVTwo(Map> varsToValidations, I18nBundle i18n){ this.varsToValidations = varsToValidations; + this.i18n = i18n; checkValidations(); } @@ -94,7 +105,7 @@ public class BasicValidationVTwo { //If no literals and this field was required, this is an error message //and can return if((literals == null || literals.size() == 0) && isRequiredField) { - errors.put(name, REQUIRED_FIELD_EMPTY_MSG); + errors.put(name, i18n.text(REQUIRED_FIELD_EMPTY_MSG)); break; } //Loop through literals if literals exist @@ -113,7 +124,7 @@ public class BasicValidationVTwo { // incorrectly generate errors. if (isEmpty(value)) { if (isRequiredField) { - errors.put(name, REQUIRED_FIELD_EMPTY_MSG); + errors.put(name, i18n.text(REQUIRED_FIELD_EMPTY_MSG)); } break; } @@ -154,11 +165,11 @@ public class BasicValidationVTwo { private String validate(String validationType, List fileItems) { if( "nonempty".equalsIgnoreCase(validationType)){ if( fileItems == null || fileItems.size() == 0 ){ - return "a file must be entered for this field."; + return i18n.text(FILE_MUST_BE_ENTERED_MSG); }else{ FileItem fileItem = fileItems.get(0); if( fileItem == null || fileItem.getName() == null || fileItem.getName().length() < 1 || fileItem.getSize() < 0){ - return "a file must be entered for this field."; + return i18n.text(FILE_MUST_BE_ENTERED_MSG); } } } @@ -174,14 +185,14 @@ public class BasicValidationVTwo { // This case may be needed for validation of other field types. if( "nonempty".equalsIgnoreCase(validationType)){ if( isEmpty(value) ) - return REQUIRED_FIELD_EMPTY_MSG; + return i18n.text(REQUIRED_FIELD_EMPTY_MSG); } // Format validation else if("isDate".equalsIgnoreCase(validationType)){ if( isDate( value)) return SUCCESS; else - return "must be in valid date format mm/dd/yyyy."; + return i18n.text(INVALID_DATE_FORM_MSG); } else if( validationType.indexOf("datatype:") == 0 ) { String datatypeURI = validationType.substring(9); @@ -194,7 +205,7 @@ public class BasicValidationVTwo { } else if ("httpUrl".equalsIgnoreCase(validationType)){ //check if it has http or https, we could do more but for now this is all. if(! value.startsWith("http://") && ! value.startsWith("https://") ){ - return "This URL must start with http:// or https://"; + return i18n.text(INVALID_URL_MSG); }else{ return SUCCESS; } @@ -216,7 +227,7 @@ public class BasicValidationVTwo { dayParamStr = value.substring(monthDash + 1, value.length()); inputC.set(Integer.parseInt(yearParamStr), Integer.parseInt(monthParamStr) - 1, Integer.parseInt(dayParamStr)); if(inputC.before(c)) { - return this.DATE_NOT_PAST_MSG; + return i18n.text(DATA_NOT_PAST_MSG); //Returning null makes the error message "field is empty" display instead //return null; } else { @@ -278,14 +289,9 @@ public class BasicValidationVTwo { return (value == null || value.trim().length() == 0); } - - - private static Pattern urlRX = Pattern.compile("(([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)/{0,2}[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)(#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?"); - /** we use null to indicate success */ public final static String SUCCESS = null; - public final static String REQUIRED_FIELD_EMPTY_MSG = "This field must not be empty."; - public final static String DATE_NOT_PAST_MSG = "Please enter a future target date for publication (past dates are invalid)."; + //public final static String MIN_FIELDS_NOT_POPULATED = "Please enter values for at least "; //public final static String FORM_ERROR_FIELD_ID = "formannotationerrors"; /** regex for strings like "12/31/2004" */ diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationUtils.java index e72e172c1..63164c0dd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationUtils.java @@ -28,6 +28,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.freemarker.config.FreemarkerConfiguration; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LanguageOption; import freemarker.template.Configuration; public class EditConfigurationUtils { @@ -61,18 +62,24 @@ public class EditConfigurationUtils { return vreq.getParameter("rangeUri"); } + public static String getTypeOfNew(VitroRequest vreq) { + return vreq.getParameter("typeOfNew"); + } + public static VClass getRangeVClass(VitroRequest vreq) { - // This needs a WebappDaoFactory with no filtering/RDFService - // funny business because it needs to be able to retrieve anonymous union - // classes by their "pseudo-bnode URIs". - // Someday we'll need to figure out a different way of doing this. WebappDaoFactory ctxDaoFact = ModelAccess.on( vreq.getSession().getServletContext()).getWebappDaoFactory(); return ctxDaoFact.getVClassDao().getVClassByURI(getRangeUri(vreq)); } + public static VClass getLangAwardRangeVClass(VitroRequest vreq) { + // UQAM-Linguistic-Management + WebappDaoFactory vreqDaoFact = ModelAccess.on(vreq).getWebappDaoFactory( + LanguageOption.LANGUAGE_AWARE); + return vreqDaoFact.getVClassDao().getVClassByURI(getRangeUri(vreq)); + } + //get individual - public static Individual getSubjectIndividual(VitroRequest vreq) { Individual subject = null; String subjectUri = getSubjectUri(vreq); @@ -122,12 +129,24 @@ public class EditConfigurationUtils { public static ObjectProperty getObjectPropertyForPredicate(VitroRequest vreq, String predicateUri, String domainUri, String rangeUri) { - WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + // WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + // UQAM-Linguistic-Management Use linguistic context + WebappDaoFactory wdf = ModelAccess.on(vreq).getWebappDaoFactory(LanguageOption.LANGUAGE_AWARE); ObjectProperty objectProp = wdf.getObjectPropertyDao().getObjectPropertyByURIs( predicateUri, domainUri, rangeUri); return objectProp; } + // UQAM Use linguistic context + public static ObjectProperty getObjectPropertyForPredicateLangAware(VitroRequest vreq, + String predicateUri, String domainUri, String rangeUri) { + // WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + // UQAM Use linguistic context + WebappDaoFactory wdf = ModelAccess.on(vreq).getWebappDaoFactory(LanguageOption.LANGUAGE_AWARE); + ObjectProperty objectProp = wdf.getObjectPropertyDao().getObjectPropertyByURIs( + predicateUri, domainUri, rangeUri); + return objectProp; + } public static DataProperty getDataPropertyForPredicate(VitroRequest vreq, String predicateUri) { WebappDaoFactory wdf = vreq.getWebappDaoFactory(); //TODO: Check reason for employing unfiltered webapp dao factory and note if using a different version @@ -205,6 +224,7 @@ public class EditConfigurationUtils { return (op != null && dp == null); } + private static boolean isVitroLabel(String predicateUri) { return predicateUri.equals(VitroVocabulary.LABEL); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java index 015652445..b81ce06d5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java @@ -366,6 +366,8 @@ public class EditN3GeneratorVTwo { { sbuff.append("@") ; sbuff.append(lang) ; + // added by UQAM to exit at this point without adding datatype + return sbuff.toString() ; } // Format the datatype diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java index 8f61f4870..306ebdce7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java @@ -10,23 +10,23 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.fileupload.FileItem; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.XSD; +import org.apache.jena.vocabulary.RDF; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; public class MultiValueEditSubmission { @@ -34,21 +34,31 @@ public class MultiValueEditSubmission { private Map> literalsFromForm ; private Map> urisFromForm ; - private Map validationErrors; private BasicValidationVTwo basicValidation; - - private Map> filesFromForm; - + private static Model literalCreationModel; - private String entityToReturnTo; + private VitroRequest _vreq; + + private static final String TIME_URI = XSD.time.getURI(); + static{ literalCreationModel = ModelFactory.createDefaultModel(); } - - public MultiValueEditSubmission(Map queryParameters, EditConfigurationVTwo editConfig){ + /* + * UQAM + * replace + * public MultiValueEditSubmission(Map queryParameters, EditConfigurationVTwo editConfig) + * by this new signature + * This affect PostEditCleanupController and ProcessRdfFormController classes. + * This replacement is justified by the fact that we need a linguistic context in this class. + */ + public MultiValueEditSubmission(VitroRequest vreq, EditConfigurationVTwo editConfig){ + // UQAM add this both lines + _vreq = vreq; + Map queryParameters = vreq.getParameterMap(); if( editConfig == null ) throw new Error("EditSubmission needs an EditConfiguration"); this.editKey = editConfig.getEditKey(); @@ -92,11 +102,12 @@ public class MultiValueEditSubmission { processEditElementFields(editConfig,queryParameters); //Incorporating basic validation //Validate URIS - this.basicValidation = new BasicValidationVTwo(editConfig, this); + this.basicValidation = new BasicValidationVTwo(editConfig, I18n.bundle(vreq)); Map errors = basicValidation.validateUris( urisFromForm ); //Validate literals and add errors to the list of existing errors errors.putAll(basicValidation.validateLiterals( literalsFromForm )); - if( errors != null ) { + // UQAM Add empty contition + if( errors != null && !errors.isEmpty()) { validationErrors.putAll( errors); } @@ -141,9 +152,8 @@ public class MultiValueEditSubmission { } } } - /* maybe this could be static */ - public Literal createLiteral(String value, String datatypeUri, String lang) { + public Literal createLiteral_ORIG(String value, String datatypeUri, String lang) { if( datatypeUri != null ){ if( "http://www.w3.org/2001/XMLSchema:anyURI".equals(datatypeUri) ){ try { @@ -159,12 +169,27 @@ public class MultiValueEditSubmission { return ResourceFactory.createPlainLiteral(value); } - private static final String DATE_TIME_URI = XSD.dateTime.getURI(); - private static final String DATE_URI = XSD.date.getURI(); - private static final String TIME_URI = XSD.time.getURI(); - - private static DateTimeFormatter dformater = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:00"); - private static DateTimeFormatter dateFormater = DateTimeFormat.forPattern("yyyy-MM-dd"); + /* maybe this could be static */ + public Literal createLiteral(String value, String datatypeUri, String lang) { + if( datatypeUri != null && !datatypeUri.isEmpty() ){ + // UQAM Original code contained tow-dots ':' in place of '#' +// if( "http://www.w3.org/2001/XMLSchema:anyURI".equals(datatypeUri) ){ + if( XSD.anyURI.getURI().equals(datatypeUri) ){ +// try { +// return literalCreationModel.createTypedLiteral( URLEncoder.encode(value, "UTF8"), datatypeUri); + return literalCreationModel.createTypedLiteral( value, datatypeUri); +// } catch (UnsupportedEncodingException e) { +// log.error(e, e); +// } + } else if ( XSD.xstring.getURI().equals(datatypeUri) || RDF.dtLangString.getURI().equals(datatypeUri) ){ + if( lang != null && lang.length() > 0 ) return ResourceFactory.createLangLiteral(value, lang); + } + return literalCreationModel.createTypedLiteral(value, datatypeUri); + // UQAM take into account the linguistic context + } else if( lang != null && lang.length() > 0 ) + return ResourceFactory.createLangLiteral(value, lang); + return ResourceFactory.createPlainLiteral(value); + } public Map getValidationErrors(){ return validationErrors; @@ -264,12 +289,46 @@ public class MultiValueEditSubmission { for(String value:valueList) { value = N3EditUtils.stripInvalidXMLChars(value); //Add to array of literals corresponding to this variable + /* UQAM OLD if (!StringUtils.isEmpty(value)) { literalsArray.add(createLiteral( value, field.getRangeDatatypeUri(), field.getRangeLang())); } + */ + /* + * UQAM Replaced by this to take the linguistic context into consideration. + */ + if (!StringUtils.isEmpty(value)) { + String rangeLang = field.getRangeLang(); //UQAM Default value + try { + if (_vreq != null ) { + // only if the request comes from the rdfsLabelGenerator the language should be used + Boolean getLabelLanguage = false; + if (!StringUtils.isBlank(editConfig.formUrl) && editConfig.formUrl.contains("RDFSLabelGenerator")) { + getLabelLanguage = true; + } + // if the language is set in the given Literal, this language-tag should be used and remain the same + // for example when you edit an label with an langauge-tag (no matter which language is selected globally) + if (!StringUtils.isBlank(editConfig.getLiteralsInScope().get("label").get(0).getLanguage()) && getLabelLanguage) + { + rangeLang = editConfig.getLiteralsInScope().get("label").get(0).getLanguage(); + } else { // if the literal has no langauge-tag, use the language which is globally selected + rangeLang = _vreq.getLocale().getLanguage(); + if (!_vreq.getLocale().getCountry().isEmpty()) { + rangeLang += "-" + _vreq.getLocale().getCountry(); + } + } + } + + } catch (Exception e) { + } + literalsArray.add(createLiteral( + value, + field.getRangeDatatypeUri(), + rangeLang)); + } } literalsFromForm.put(var, literalsArray); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfForm.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfForm.java index 54fd7fb1b..9652d72dc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfForm.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfForm.java @@ -9,15 +9,24 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.ontology.OntModel; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.rdf.model.Statement; import org.apache.jena.shared.Lock; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.XSD; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; @@ -41,6 +50,7 @@ public class ProcessRdfForm { private EditN3GeneratorVTwo populator; private Map urisForNewResources = null; +// private VitroRequest _vreq; /** * Construct the ProcessRdfForm object. */ @@ -76,9 +86,9 @@ public class ProcessRdfForm { AdditionsAndRetractions changes; if( configuration.isUpdate() ){ - changes = editExistingStatements(configuration, submission); + changes = editExistingStatements(configuration, submission, vreq); //UQAM vreq for getting linguistic context } else { - changes = createNewStatements(configuration, submission ); + changes = createNewStatements(configuration, submission, vreq ); //UQAM vreq for getting linguistic context } changes = getMinimalChanges(changes); @@ -99,12 +109,15 @@ public class ProcessRdfForm { * any optional N3 is to originally configure the * configuration.setN3Optional() to be empty. * + * UQAM add vreq for linguistic context managing + * * @throws Exception May throw an exception if the required N3 * does not parse. + * */ private AdditionsAndRetractions createNewStatements( EditConfigurationVTwo configuration, - MultiValueEditSubmission submission) throws Exception { + MultiValueEditSubmission submission, VitroRequest vreq) throws Exception { log.debug("in createNewStatements()" ); //getN3Required and getN3Optional will return copies of the @@ -113,10 +126,10 @@ public class ProcessRdfForm { List optionalN3 = configuration.getN3Optional(); /* substitute in the form values and existing values */ - subInValuesToN3( configuration, submission, requiredN3, optionalN3, null , null); + subInValuesToN3( configuration, submission, requiredN3, optionalN3, null , null, vreq); /* parse N3 to RDF Models, No retractions since all of the statements are new. */ - return parseN3ToChange(requiredN3, optionalN3, null, null); + return parseN3ToChange(requiredN3, optionalN3, null, null, vreq, null); } /* for a list of N3 strings, substitute in the subject, predicate and object URIs @@ -140,10 +153,12 @@ public class ProcessRdfForm { * retractions are mutually diff'ed before statements are added to or * removed from the model. The explicit change check can cause problems in * more complex setups, like the automatic form building in DataStaR. + * @param vreq For getting linguistic context + */ protected AdditionsAndRetractions editExistingStatements( EditConfigurationVTwo editConfig, - MultiValueEditSubmission submission) throws Exception { + MultiValueEditSubmission submission, VitroRequest vreq) throws Exception { log.debug("editing an existing resource: " + editConfig.getObject() ); @@ -156,18 +171,18 @@ public class ProcessRdfForm { subInValuesToN3(editConfig, submission, N3RequiredAssert, N3OptionalAssert, - N3RequiredRetract, N3OptionalRetract); + N3RequiredRetract, N3OptionalRetract, vreq); return parseN3ToChange( N3RequiredAssert,N3OptionalAssert, - N3RequiredRetract, N3OptionalRetract); + N3RequiredRetract, N3OptionalRetract, vreq, editConfig); } @SuppressWarnings("unchecked") protected void subInValuesToN3( EditConfigurationVTwo editConfig, MultiValueEditSubmission submission, List requiredAsserts, List optionalAsserts, - List requiredRetracts, List optionalRetracts ) throws InsertException{ + List requiredRetracts, List optionalRetracts, VitroRequest vreq ) throws InsertException{ //need to substitute into the return to URL becase it may need new resource URIs List URLToReturnTo = Arrays.asList(submission.getEntityToReturnTo()); @@ -184,7 +199,41 @@ public class ProcessRdfForm { //Retractions does NOT get values from form. /* ******** Form submission Literals *********** */ - substituteInMultiLiterals( submission.getLiteralsFromForm(), requiredAsserts, optionalAsserts, URLToReturnTo); + /* + * UQAM Set all literals in the linguistic context + */ + Map> literalsFromForm = submission.getLiteralsFromForm(); + Set keys = literalsFromForm.keySet(); + for (String aKey : keys) { + List literalFromForm = literalsFromForm.get(aKey); + List newLiteralFromForm = new ArrayList<>(); + for (Literal aLiteral : literalFromForm) { + if (aLiteral != null) { + String aLiteratDT = aLiteral.getDatatype().getURI(); + Literal newLiteral = null; + String aText = aLiteral.getLexicalForm(); + /* + * do it only if aLiteral are xstring datatype + */ + if (RDF.dtLangString.getURI().equals(aLiteratDT) && !aLiteral.getLanguage().isEmpty()) { + newLiteral = aLiteral; + } + else if (XSD.xstring.getURI().equals(aLiteratDT) || RDF.dtLangString.getURI().equals(aLiteratDT)) { + String lang = vreq.getLocale().getLanguage(); + if (!vreq.getLocale().getCountry().isEmpty()) { + lang += "-" + vreq.getLocale().getCountry(); + } + newLiteral = ResourceFactory.createLangLiteral(aText, lang); + } else { + newLiteral = ResourceFactory.createTypedLiteral(aText, aLiteral.getDatatype()); + } + newLiteralFromForm.add(newLiteral); + } + } + literalsFromForm.replace(aKey, newLiteralFromForm); + } + + substituteInMultiLiterals( literalsFromForm, requiredAsserts, optionalAsserts, URLToReturnTo); logSubstitue( "Added form Literals", requiredAsserts, optionalAsserts, requiredRetracts, optionalRetracts); //Retractions does NOT get values from form. @@ -254,20 +303,106 @@ public class ProcessRdfForm { protected AdditionsAndRetractions parseN3ToChange( List requiredAdds, List optionalAdds, - List requiredDels, List optionalDels) throws Exception{ + List requiredDels, List optionalDels, VitroRequest vreq, EditConfigurationVTwo editConfig) throws Exception{ List adds = parseN3ToRDF(requiredAdds, REQUIRED); adds.addAll( parseN3ToRDF(optionalAdds, OPTIONAL)); - List retracts = new ArrayList(); if( requiredDels != null && optionalDels != null ){ - retracts.addAll( parseN3ToRDF(requiredDels, REQUIRED) ); - retracts.addAll( parseN3ToRDF(optionalDels, OPTIONAL) ); + String lingCxt=null; + //UQAM Taking into account the linguistic context in retract + try { + // only if the request comes from the rdfsLabelGenerator the language should be used + Boolean getLabelLanguage = false; + if (!StringUtils.isBlank(editConfig.formUrl) && editConfig.formUrl.contains("RDFSLabelGenerator")) { + getLabelLanguage = true; + } + // if the language is set in the given Literal, this language-tag should be used and remain the same + // for example when you edit an label with an langauge-tag (no matter which language is selected globally) + if (editConfig != null && !StringUtils.isBlank(editConfig.getLiteralsInScope().get("label").get(0).getLanguage()) && getLabelLanguage) { + lingCxt = editConfig.getLiteralsInScope().get("label").get(0).getLanguage(); + } else { // if the literal has no langauge-tag, use the language which is globally selected + lingCxt = vreq.getLocale().getLanguage(); + if (!vreq.getLocale().getCountry().isEmpty()) { + lingCxt += "-" + vreq.getLocale().getCountry(); + } + } + } catch (Exception e) { + } + retracts.addAll( parseN3ToRDF(requiredDels, REQUIRED, lingCxt) ); + retracts.addAll( parseN3ToRDF(optionalDels, OPTIONAL, lingCxt) ); } return new AdditionsAndRetractions(adds,retracts); } + /** + * Parse the n3Strings to a List of RDF Model objects. + * + * @param n3Strings N3 Strings to parse + * @param parseType if OPTIONAL, then don't throw exceptions on errors + * @param linguisticContext For Literals, Making parse only if the literal linguisticContext are same than linguisticContext parameter //UQAM + * If REQUIRED, then throw exceptions on errors. + * @throws Exception + */ + protected static List parseN3ToRDF( + List n3Strings, N3ParseType parseType, String linguisticContext ) throws Exception { + // Use non-linguistic version of this method if no linguisticContext is provided + if (linguisticContext == null) { + return parseN3ToRDF(n3Strings, parseType); + } + List errorMessages = new ArrayList(); + + List rdfModels = new ArrayList(); + for(String n3 : n3Strings){ + try{ + Model model = ModelFactory.createDefaultModel(); + StringReader reader = new StringReader(n3); + model.read(reader, "", "N3"); + List stmts = model.listStatements().toList(); + for (Iterator iterator = stmts.iterator(); iterator.hasNext();) { + Statement stmt = (Statement) iterator.next(); + Resource subj = stmt.getSubject(); + Property pred = stmt.getPredicate(); + RDFNode obj = stmt.getObject(); + if (obj.isLiteral()) { + Literal lit = obj.asLiteral(); + String lang = lit.getLanguage(); + if (! linguisticContext.equals(lang)) { + //UQAM Remove if linguisticContext != lang of the Literal + model.remove(subj, pred, obj); + } + } + + } + rdfModels.add( model ); + }catch(Throwable t){ + errorMessages.add(t.getMessage() + "\nN3: \n" + n3 + "\n"); + } + } + + StringBuilder errors = new StringBuilder(); + for( String errorMsg : errorMessages){ + errors.append(errorMsg).append('\n'); + } + + if( !errorMessages.isEmpty() ){ + if( REQUIRED.equals(parseType) ){ + throw new Exception("Errors processing required N3. The EditConfiguration should " + + "be setup so that if a submission passes validation, there will not be errors " + + "in the required N3.\n" + errors ); + }else if( OPTIONAL.equals(parseType) ){ + log.debug("Some Optional N3 did not parse, if a optional N3 does not parse it " + + "will be ignored. This allows optional parts of a form submission to " + + "remain unfilled out and then the optional N3 does not get values subsituted in from" + + "the form submission values. It may also be the case that there are unintentional " + + "syntax errors the optional N3." ); + log.debug(errors.toString()); + } + } + + return rdfModels; + } /** * Parse the n3Strings to a List of RDF Model objects. * @@ -479,4 +614,4 @@ public class ProcessRdfForm { private static Log log = LogFactory.getLog(ProcessRdfForm.class); -} +} \ No newline at end of file diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesOptions.java index f87dd8f48..0c7b44ef2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesOptions.java @@ -7,6 +7,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -40,7 +41,8 @@ public class ChildVClassesOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception{ + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception{ // now create an empty HashMap to populate and return HashMap optionsMap = new LinkedHashMap(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java index 135b38474..22f5763a4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java @@ -1,6 +1,6 @@ /* $This file is distributed under the terms of the license in LICENSE$ */ -package edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields; +package edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; @@ -15,6 +15,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; public class ChildVClassesWithParent implements FieldOptions { @@ -37,20 +38,24 @@ public class ChildVClassesWithParent implements FieldOptions { return this; } - @Override +/* + * UQAM-Linguistic-Management + * This method is polymorphism of getOptions(EditConfigurationVTwo editConfig,String fieldName, WebappDaoFactory wDaoFact) + * for the internationalization of word "other" in the scroling list of personHasAdvisorRelationship.ftl + */ public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception { - + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception { HashMap optionsMap = new LinkedHashMap(); // first test to see whether there's a default "leave blank" value specified with the literal options if ( ! StringUtils.isEmpty( defaultOptionLabel ) ){ optionsMap.put(LEFT_BLANK, defaultOptionLabel); } - - optionsMap.put(classUri, "Other"); - + String other_i18n = i18n.text("other"); + // first character in capital + optionsMap.put(classUri, other_i18n.substring(0, 1).toUpperCase() + other_i18n.substring(1)); VClassDao vclassDao = wDaoFact.getVClassDao(); List subClassList = vclassDao.getAllSubClassURIs(classUri); if (subClassList != null && subClassList.size() > 0) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ConstantFieldOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ConstantFieldOptions.java index 3c68ac14d..9fc7662d5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ConstantFieldOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ConstantFieldOptions.java @@ -10,6 +10,7 @@ import java.util.Map; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; public class ConstantFieldOptions implements FieldOptions { @@ -54,7 +55,8 @@ public class ConstantFieldOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception { + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception { // originally not auto-sorted but sorted now, and empty values not removed or replaced HashMap optionsMap = new LinkedHashMap(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldOptions.java index b2c67f549..772b22913 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldOptions.java @@ -6,6 +6,7 @@ import java.util.Map; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; /** * Represents an object that can return a list of options @@ -28,7 +29,8 @@ public interface FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception; + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception; /* * Certain field options may have custom sorting requirements. If no sorting requirements exist, diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaClassGroupOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaClassGroupOptions.java index b0a499005..fa627a12e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaClassGroupOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaClassGroupOptions.java @@ -6,6 +6,7 @@ import java.util.Map; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; public class IndividualsViaClassGroupOptions implements FieldOptions { @@ -27,7 +28,8 @@ public class IndividualsViaClassGroupOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception { + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception { throw new Error("not implemented"); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java index 6a5d30be5..0cd216ef7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java @@ -11,6 +11,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -77,7 +78,8 @@ public class IndividualsViaObjectPropetyOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) { + WebappDaoFactory wDaoFact, + I18nBundle i18n) { HashMap optionsMap = new LinkedHashMap(); int optionsCount = 0; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaSearchQueryOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaSearchQueryOptions.java index ec7167c58..d752bfe28 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaSearchQueryOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaSearchQueryOptions.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -90,7 +91,8 @@ public class IndividualsViaSearchQueryOptions extends IndividualsViaVClassOption public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception { + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception { Map individualMap = new HashMap(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaVClassOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaVClassOptions.java index 80c9cf273..374d571ed 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaVClassOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaVClassOptions.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,7 +49,8 @@ public class IndividualsViaVClassOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact) throws Exception { + WebappDaoFactory wDaoFact, + I18nBundle i18n) throws Exception { Map individualMap = new HashMap(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/RdfTypeOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/RdfTypeOptions.java index 994385673..32c520fd3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/RdfTypeOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/RdfTypeOptions.java @@ -9,6 +9,7 @@ import java.util.Map; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; public class RdfTypeOptions implements FieldOptions { @@ -29,7 +30,8 @@ public class RdfTypeOptions implements FieldOptions { public Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wdf) { + WebappDaoFactory wdf, + I18nBundle i18n) { Map uriToLabel = new HashMap(); for (String uri : typeURIs) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/SelectListGeneratorVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/SelectListGeneratorVTwo.java index 3e732ce58..c554cf39e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/SelectListGeneratorVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/SelectListGeneratorVTwo.java @@ -11,6 +11,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -25,7 +27,8 @@ public class SelectListGeneratorVTwo { public static Map getOptions( EditConfigurationVTwo editConfig, String fieldName, - WebappDaoFactory wDaoFact){ + WebappDaoFactory wDaoFact, + I18nBundle i18n){ if( editConfig == null ){ @@ -48,13 +51,51 @@ public class SelectListGeneratorVTwo { } try { - return field.getFieldOptions().getOptions(editConfig,fieldName,wDaoFact); + return field.getFieldOptions().getOptions(editConfig,fieldName,wDaoFact,i18n); } catch (Exception e) { log.error("Error runing getFieldOptionis()",e); return Collections.emptyMap(); } } + // UQAM Overcharge method for linguistic contexte processisng + // AWoods: This method appears to never be invoked. + public static Map getOptions( + EditConfigurationVTwo editConfig, + String fieldName, + VitroRequest vreq){ + + + if( editConfig == null ){ + log.error( "fieldToSelectItemList() must be called with a non-null EditConfigurationVTwo "); + return Collections.emptyMap(); + } + if( fieldName == null ){ + log.error( "fieldToSelectItemList() must be called with a non-null fieldName"); + return Collections.emptyMap(); + } + + FieldVTwo field = editConfig.getField(fieldName); + if (field==null) { + log.error("no field \""+fieldName+"\" found from editConfig."); + return Collections.emptyMap(); + } + + if( field.getFieldOptions() == null ){ + return Collections.emptyMap(); + } + + try { + //UQAM need vreq instead of WebappDaoFactory + Map parentClass = Collections.emptyMap(); + FieldOptions fieldOptions = field.getFieldOptions(); + return fieldOptions.getOptions(editConfig,fieldName,vreq.getWebappDaoFactory(), I18n.bundle(vreq)); + } catch (Exception e) { + log.error("Error runing getFieldOptionis()",e); + return Collections.emptyMap(); + } + } + //Methods to sort the options map // from http://forum.java.sun.com/thread.jspa?threadID=639077&messageID=4250708 diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java index f5de55789..55cff7761 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java @@ -16,6 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTw import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.IdModelSelector; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.StandardModelSelector; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LanguageOption; public abstract class BaseEditConfigurationGenerator implements EditConfigurationGenerator { @@ -63,6 +64,7 @@ public abstract class BaseEditConfigurationGenerator implements EditConfiguratio setupModelSelectorsFromVitroRequest(vreq, editConfig); OntModel queryModel = ModelAccess.on(vreq).getOntModel(); + OntModel languageNeutralModel = vreq.getLanguageNeutralUnionFullModel(); if( editConfig.getSubjectUri() == null) editConfig.setSubjectUri( EditConfigurationUtils.getSubjectUri(vreq)); @@ -78,7 +80,10 @@ public abstract class BaseEditConfigurationGenerator implements EditConfiguratio editConfig.prepareForObjPropUpdate(queryModel); } else if( dataKey != null ) { // edit of a data prop statement //do nothing since the data prop form generator must take care of it - editConfig.prepareForDataPropUpdate(queryModel, vreq.getWebappDaoFactory().getDataPropertyDao()); + // Use language-neutral model to ensure that a data property statement + // is found for any literal hash, even if the UI locale is changed. + editConfig.prepareForDataPropUpdate(languageNeutralModel, + vreq.getWebappDaoFactory().getDataPropertyDao()); } else{ //this might be a create new or a form editConfig.prepareForNonUpdate(queryModel); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java index 702474da2..5537cecac 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java @@ -350,7 +350,7 @@ public class DefaultAddMissingIndividualFormGenerator implements EditConfigurati private void prepareForUpdate(VitroRequest vreq, HttpSession session, EditConfigurationVTwo editConfiguration) { //Here, retrieve model from - OntModel model = ModelAccess.on(session.getServletContext()).getOntModel(); + OntModel model = ModelAccess.on(vreq).getOntModel(); //if object property if(EditConfigurationUtils.isObjectProperty(EditConfigurationUtils.getPredicateUri(vreq), vreq)){ Individual objectIndividual = EditConfigurationUtils.getObjectIndividual(vreq); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java index ce471ca98..adb7e8bb8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java @@ -2,6 +2,8 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LANGUAGE_NEUTRAL; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.POLICY_NEUTRAL; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY; import java.util.ArrayList; @@ -35,6 +37,8 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.IndividualsVi import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation; import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LanguageOption; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.PolicyOption; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineException; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery; @@ -125,7 +129,10 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene // Someday we'll need to figure out a different way of doing this. //WebappDaoFactory ctxDaoFact = ModelAccess.on( // vreq.getSession().getServletContext()).getWebappDaoFactory(); - WebappDaoFactory ctxDaoFact = vreq.getLanguageNeutralWebappDaoFactory(); +// WebappDaoFactory ctxDaoFact = vreq.getLanguageNeutralWebappDaoFactory(); + //UQAM Linguistic-Management Manage linguistic context + WebappDaoFactory ctxDaoFact = ModelAccess.on(vreq).getWebappDaoFactory(LanguageOption.LANGUAGE_AWARE, PolicyOption.POLICY_NEUTRAL); + List types = new ArrayList(); Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq); @@ -460,7 +467,7 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene private void prepareForUpdate(VitroRequest vreq, HttpSession session, EditConfigurationVTwo editConfiguration) { //Here, retrieve model from - OntModel model = ModelAccess.on(session.getServletContext()).getOntModel(); + OntModel model = ModelAccess.on(vreq).getOntModel(); //if object property if(EditConfigurationUtils.isObjectProperty(EditConfigurationUtils.getPredicateUri(vreq), vreq)){ Individual objectIndividual = EditConfigurationUtils.getObjectIndividual(vreq); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManageLabelsForIndividualGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManageLabelsForIndividualGenerator.java index 15782747b..3f7e912bb 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManageLabelsForIndividualGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManageLabelsForIndividualGenerator.java @@ -15,6 +15,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import javax.servlet.http.HttpSession; @@ -41,6 +42,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.FoafNameToRdfsLabelPreprocessor; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.ManageLabelsForIndividualPreprocessor; import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DataPropertyStatementTemplateModel; /** @@ -202,12 +204,12 @@ public class ManageLabelsForIndividualGenerator extends BaseEditConfigurationGen private void addFormSpecificData(EditConfigurationVTwo config, VitroRequest vreq) { - //Get all language codes/labels in the system, and this list is sorted by language name - List> locales = this.getLocales(vreq); + //the labels already added by the user + ArrayList existingLabels = this.getExistingLabels(config.getSubjectUri(), vreq); + //Get language codes/labels for languages present in the existing labels + List> locales = this.getLocales(vreq, existingLabels); //Get code to label hashmap - we use this to get the language name for the language code returned in the rdf literal - HashMap localeCodeToNameMap = this.getFullCodeToLanguageNameMap(locales); - //the labels already added by the user - ArrayList existingLabels = this.getExistingLabels(config.getSubjectUri(), vreq); + HashMap localeCodeToNameMap = this.getFullCodeToLanguageNameMap(locales); int numberExistingLabels = existingLabels.size(); //existing labels keyed by language name and each of the list of labels is sorted by language name HashMap> existingLabelsByLanguageName = this.getLabelsSortedByLanguageName(existingLabels, localeCodeToNameMap, config, vreq); @@ -224,6 +226,20 @@ public class ManageLabelsForIndividualGenerator extends BaseEditConfigurationGen config.addFormSpecificData("displayRemoveLink", (numberExistingLabels > 1)); + // get current selected locale + String rangeLang = vreq.getLocale().getLanguage(); + if (!vreq.getLocale().getCountry().isEmpty()) { + rangeLang += "-" + vreq.getLocale().getCountry(); + } + + // check if locale already has an entry (label) + boolean localeEntryExisting = true; + for (HashMap tmp : availableLocalesForAdd) { + if (tmp.get("code").equals(rangeLang)) localeEntryExisting = false; + } + config.addFormSpecificData("localeEntryExisting", localeEntryExisting); + config.addFormSpecificData("currentSelectedLocale", rangeLang); + //How do we edit? Will need to see config.addFormSpecificData("deleteWebpageUrl", "/edit/primitiveDelete"); @@ -359,8 +375,9 @@ public class ManageLabelsForIndividualGenerator extends BaseEditConfigurationGen ArrayList labels = new ArrayList(); try { - //We want to get the labels for all the languages, not just the display language - ResultSet results = QueryUtils.getLanguageNeutralQueryResults(queryStr, vreq); + // Get results filtered to current locale so as to be consistent + // with other editing forms. + ResultSet results = QueryUtils.getQueryResults(queryStr, vreq); while (results.hasNext()) { QuerySolution soln = results.nextSolution(); Literal nodeLiteral = soln.get("label").asLiteral(); @@ -387,30 +404,32 @@ public class ManageLabelsForIndividualGenerator extends BaseEditConfigurationGen return template; } + //get locales present in list of literals + public List> getLocales(VitroRequest vreq, + List existingLiterals) { + Set locales = new HashSet(); + for(Literal literal : existingLiterals) { + String language = literal.getLanguage(); + if(!StringUtils.isEmpty(language)) { + locales.add(LanguageFilteringUtils.languageToLocale(language)); + } + } + if (locales.isEmpty()) { + return Collections.emptyList(); + } + List> list = new ArrayList>(); + Locale currentLocale = SelectedLocale.getCurrentLocale(vreq); + for (Locale locale : locales) { + try { + list.add(buildLocaleMap(locale, currentLocale)); + } catch (FileNotFoundException e) { + log.warn("Can't show locale '" + locale + "': " + e); + } + } - - //get locales - public List> getLocales(VitroRequest vreq) { - List selectables = SelectedLocale.getSelectableLocales(vreq); - if (selectables.isEmpty()) { - return Collections.emptyList(); - } - List> list = new ArrayList>(); - Locale currentLocale = SelectedLocale.getCurrentLocale(vreq); - for (Locale locale : selectables) { - try { - list.add(buildLocaleMap(locale, currentLocale)); - } catch (FileNotFoundException e) { - log.warn("Can't show the Locale selector for '" + locale - + "': " + e); - } - } - - return list; + return list; } - - public HashMap getFullCodeToLanguageNameMap(List> localesList) { HashMap codeToLanguageMap = new HashMap(); for(Map locale: localesList) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/NewIndividualFormGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/NewIndividualFormGenerator.java index 8c251467b..fff4d6732 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/NewIndividualFormGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/NewIndividualFormGenerator.java @@ -233,7 +233,7 @@ public class NewIndividualFormGenerator implements EditConfigurationGenerator { private void prepareForUpdate(VitroRequest vreq, HttpSession session, EditConfigurationVTwo editConfiguration) { //Here, retrieve model from - OntModel model = ModelAccess.on(session.getServletContext()).getOntModel(); + OntModel model = ModelAccess.on(vreq).getOntModel(); //This form is always doing a non-update editConfiguration.prepareForNonUpdate( model ); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/RDFSLabelGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/RDFSLabelGenerator.java index 3a3e7f371..13e845a86 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/RDFSLabelGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/RDFSLabelGenerator.java @@ -298,7 +298,7 @@ public class RDFSLabelGenerator implements EditConfigurationGenerator { private void prepareForUpdate(VitroRequest vreq, HttpSession session, EditConfigurationVTwo editConfiguration) { //Here, retrieve model from - OntModel model = ModelAccess.on(session.getServletContext()).getOntModel(); + OntModel model = ModelAccess.on(vreq).getOntModel(); if( editConfiguration.isDataPropertyUpdate() ){ editConfiguration.prepareForDataPropUpdate(model, vreq.getWebappDaoFactory().getDataPropertyDao()); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguage.java new file mode 100644 index 000000000..acd94dfae --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguage.java @@ -0,0 +1,92 @@ +package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; + +/** + * A model change preprocessor that discards triples with language tags + * in the retractionsModel that do not match the specified language, unless + * the additionsModel also contains a new value for the same subject and + * predicate in that language, or no values in any language are added for the + * same subject and predicate (such as when an entire resource is deleted) . + */ +public class LimitRemovalsToLanguage implements ModelChangePreprocessor { + + private static final Log log = LogFactory.getLog(LimitRemovalsToLanguage.class); + private String language; + + /** + * @param locale the Java locale object representing the language + * to which edits should be limited. May not be null. + */ + public LimitRemovalsToLanguage(Locale locale) { + if(locale == null) { + throw new IllegalArgumentException("Locale may not be null."); + } + this.language = LanguageFilteringUtils.localeToLanguage(locale); + } + + /** + * @param language string representing the RDF language tag to which + * edits should be limited. May not be null. + */ + public LimitRemovalsToLanguage(String language) { + if(language == null) { + throw new IllegalArgumentException("Language may not be null."); + } + this.language = language; + } + + @Override + public void preprocess(Model retractionsModel, Model additionsModel, + HttpServletRequest request) { + log.debug("limiting changes to " + language); + List eliminatedRetractions = new ArrayList(); + StmtIterator sit = retractionsModel.listStatements(); + while(sit.hasNext()) { + Statement stmt = sit.next(); + if(stmt.getObject().isLiteral()) { + Literal lit = stmt.getObject().asLiteral(); + if(!StringUtils.isEmpty(lit.getLanguage()) + && !lit.getLanguage().equals(language)) { + boolean eliminateRetraction = true; + StmtIterator replacements = additionsModel + .listStatements(stmt.getSubject(), + stmt.getPredicate(), (RDFNode) null); + if(!replacements.hasNext()) { + eliminateRetraction = false; + } else { + while(replacements.hasNext()) { + Statement replacement = replacements.next(); + if(replacement.getObject().isLiteral() + && lit.getLanguage().equals(replacement + .getObject().asLiteral() + .getLanguage())) { + eliminateRetraction = false; + } + } + } + if(eliminateRetraction) { + eliminatedRetractions.add(stmt); + } + } + } + } + retractionsModel.remove(eliminatedRetractions); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java index 96d6556b8..16b1122fb 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java @@ -11,6 +11,7 @@ import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jena.ontology.OntModel; @@ -67,10 +68,17 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet { //TODO: Create this generator final String RDFS_LABEL_FORM = "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.RDFSLabelGenerator"; final String DEFAULT_DELETE_FORM = "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.DefaultDeleteGenerator"; - - @Override - protected AuthorizationRequest requiredActions(VitroRequest vreq) { - //Check if this statement can be edited here and return unauthorized if not + final String MANAGE_MENUS_FORM = "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.ManagePageGenerator"; + + @Override + protected AuthorizationRequest requiredActions(VitroRequest vreq) { + // If request is for new individual, return simple do back end editing action permission + if (StringUtils.isNotEmpty(EditConfigurationUtils.getTypeOfNew(vreq))) { + return SimplePermission.DO_BACK_END_EDITING.ACTION; + } else if(MANAGE_MENUS_FORM.equals(vreq.getParameter("editForm"))) { + return SimplePermission.MANAGE_MENUS.ACTION; + } + // Check if this statement can be edited here and return unauthorized if not String subjectUri = EditConfigurationUtils.getSubjectUri(vreq); String predicateUri = EditConfigurationUtils.getPredicateUri(vreq); String objectUri = EditConfigurationUtils.getObjectUri(vreq); @@ -148,7 +156,7 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet { templateData.put("editConfiguration", etm); templateData.put("editSubmission", submissionTemplateModel); //Corresponding to original note for consistency with selenium tests and 1.1.1 - templateData.put("title", "Edit"); + templateData.put("title", etm.getPageTitle()); templateData.put("submitUrl", getSubmissionUrl(vreq)); templateData.put("cancelUrl", etm.getCancelUrl()); templateData.put("editKey", editConfig.getEditKey()); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/PostEditCleanupController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/PostEditCleanupController.java index 64f605103..b1336b50f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/PostEditCleanupController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/PostEditCleanupController.java @@ -74,9 +74,11 @@ public class PostEditCleanupController extends FreemarkerHttpServlet{ //The submission for getting the entity to return to is not retrieved from the session but needs //to be created - as it is in processRdfForm3.jsp if( entityToReturnTo == null ){ - //this will not work if there entityToReturnTo has a new resource URI, - //in that case entityToReturnTo should not have been passed to this method as null - MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq.getParameterMap(), editConfig); + // this will not work if there entityToReturnTo has a new resource URI, + // in that case entityToReturnTo should not have been passed to this method as null + // UQAM-Linguistic-Management + // MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq.getParameterMap(), editConfig); + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, editConfig); entityToReturnTo = N3EditUtils.processEntityToReturnTo(editConfig, submission, vreq); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java index 790781a95..1c82b034b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java @@ -8,10 +8,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.servlet.annotation.WebServlet; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.Property; @@ -37,8 +38,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.MultiValueEditSubmis import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.N3EditUtils; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.ProcessRdfForm; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.RdfLiteralHash; - -import javax.servlet.annotation.WebServlet; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.LimitRemovalsToLanguage; /** * This servlet will convert a request to an EditSubmission, @@ -66,7 +66,9 @@ public class ProcessRdfFormController extends FreemarkerHttpServlet{ return handleMissingConfiguration(vreq); //get the EditSubmission - MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq.getParameterMap(), configuration); + // MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq.getParameterMap(), configuration); + // Modified by UQAM-Linguistic-Management + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, configuration); EditSubmissionUtils.putEditSubmissionInSession(vreq.getSession(), submission); //if errors, return error response @@ -101,8 +103,11 @@ public class ProcessRdfFormController extends FreemarkerHttpServlet{ if( configuration.isUseDependentResourceDelete() ) changes = ProcessRdfForm.addDependentDeletes(changes, queryModel); + // prevent form from removing literals in languages other than the one + // associated with the current request + configuration.addModelChangePreprocessor(new LimitRemovalsToLanguage(vreq.getLocale())); N3EditUtils.preprocessModels(changes, configuration, vreq); - + ProcessRdfForm.applyChangesToWriteModel(changes, queryModel, writeModel, N3EditUtils.getEditorUri(vreq) ); //Here we are trying to get the entity to return to URL, diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java index 7c0813275..7e5f49a58 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java @@ -35,6 +35,7 @@ import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService; import edu.cornell.mannlib.vitro.webapp.modelaccess.impl.RequestModelAccessImpl; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringUtils; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; /** @@ -88,8 +89,10 @@ public class ModelSwitcher { // If they asked for the display model, give it to them. if (isParameterPresent(vreq, SWITCH_TO_DISPLAY_MODEL)) { - OntModel mainOntModel = ModelAccess.on(_context).getOntModel(DISPLAY); - OntModel tboxOntModel = ModelAccess.on(_context).getOntModel(DISPLAY_TBOX); + OntModel mainOntModel = LanguageFilteringUtils.wrapOntModelInALanguageFilter( + ModelAccess.on(vreq).getOntModel(DISPLAY), vreq); + OntModel tboxOntModel = LanguageFilteringUtils.wrapOntModelInALanguageFilter( + ModelAccess.on(vreq).getOntModel(DISPLAY_TBOX), vreq); setSpecialWriteModel(vreq, mainOntModel); vreq.setAttribute(VitroRequest.ID_FOR_ABOX_MODEL, VitroModelSource.ModelName.DISPLAY.toString()); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/PageRoutingFilter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/PageRoutingFilter.java index d956483f6..0f739361c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/PageRoutingFilter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/PageRoutingFilter.java @@ -63,6 +63,7 @@ public class PageRoutingFilter implements Filter{ // get URL without hostname or servlet context HttpServletResponse response = (HttpServletResponse) arg1; HttpServletRequest req = (HttpServletRequest) arg0; + String path = req.getRequestURI().substring(req.getContextPath().length()); // check for first part of path diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java index c0ab92d25..719b041ab 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java @@ -6,6 +6,7 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.servlet.ServletContext; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java index 8f07f7301..85f58495d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java @@ -8,6 +8,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; +import java.util.Objects; import java.util.ResourceBundle; import java.util.SortedSet; import java.util.TreeSet; @@ -20,7 +21,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; @@ -49,12 +52,27 @@ public class I18n { * This is where the work gets done. Not declared final, so it can be * modified in unit tests. */ - private static I18n instance = new I18n(); + private static I18n instance; + + private final ServletContext ctx; + + protected I18n(ServletContext ctx) { + this.ctx = ctx; + } // ---------------------------------------------------------------------- // Static methods // ---------------------------------------------------------------------- + /** + * This setup method must be called before I18n static methods can be used. + * It is currently called from LocaleSelectionSetup.contextInitialized, which + * should ensure it is called when the context is initialized. + */ + public static void setup(ServletContext ctx) { + I18n.instance = new I18n(ctx); + } + /** * A convenience method to get a bundle and format the text. */ @@ -72,19 +90,26 @@ public class I18n { } /** - * Get a I18nBundle by this name. + * Get a request I18nBundle by this name. */ public static I18nBundle bundle(String bundleName, HttpServletRequest req) { return instance.getBundle(bundleName, req); } /** - * Get the default I18nBundle. + * Get the default request I18nBundle. */ public static I18nBundle bundle(HttpServletRequest req) { return instance.getBundle(DEFAULT_BUNDLE_NAME, req); } + /** + * Get the default context I18nBundle for preferred locales. + */ + public static I18nBundle bundle(List preferredLocales) { + return instance.getBundle(DEFAULT_BUNDLE_NAME, preferredLocales); + } + // ---------------------------------------------------------------------- // The instance // ---------------------------------------------------------------------- @@ -106,25 +131,59 @@ public class I18n { * Declared 'protected' so it can be overridden in unit tests. */ protected I18nBundle getBundle(String bundleName, HttpServletRequest req) { - log.debug("Getting bundle '" + bundleName + "'"); + log.debug("Getting request bundle '" + bundleName + "'"); + checkDevelopmentMode(req); + checkForChangeInThemeDirectory(req); + + Locale locale = req.getLocale(); + + return getBundle(bundleName, locale); + } + + /** + * Get an I18nBundle by this name. The context provides the selectable + * Locale, the application directory, the theme directory and the + * development mode flag. Choosing matching locale from context by + * provided preferred locales. + * + * If the context indicates that the system is in development mode, then the + * cache is cleared on each request. + * + * If the theme directory has changed, the cache is cleared. + * + * Declared 'protected' so it can be overridden in unit tests. + */ + protected I18nBundle getBundle(String bundleName, List preferredLocales) { + log.debug("Getting context bundle '" + bundleName + "'"); + + checkDevelopmentMode(); + checkForChangeInThemeDirectory(ctx); + + Locale locale = SelectedLocale.getPreferredLocale(ctx, preferredLocales); + + return getBundle(bundleName, locale); + } + + /** + * Get an I18nBundle by this name, context, and locale. + */ + private I18nBundle getBundle(String bundleName, Locale locale) { I18nLogger i18nLogger = new I18nLogger(); try { - checkDevelopmentMode(req); - checkForChangeInThemeDirectory(req); - String dir = themeDirectory.get(); - ServletContext ctx = req.getSession().getServletContext(); - ResourceBundle.Control control = new ThemeBasedControl(ctx, dir); ResourceBundle rb = ResourceBundle.getBundle(bundleName, - req.getLocale(), control); + locale, control); + return new I18nBundle(bundleName, rb, i18nLogger); } catch (MissingResourceException e) { log.warn("Didn't find text bundle '" + bundleName + "'"); + return I18nBundle.emptyBundle(bundleName, i18nLogger); } catch (Exception e) { log.error("Failed to create text bundle '" + bundleName + "'", e); + return I18nBundle.emptyBundle(bundleName, i18nLogger); } } @@ -139,6 +198,16 @@ public class I18n { } } + /** + * If we are in development mode, clear the cache. + */ + private void checkDevelopmentMode() { + if (DeveloperSettings.getInstance().getBoolean(Key.I18N_DEFEAT_CACHE)) { + log.debug("In development mode - clearing the cache."); + ResourceBundle.clearCache(); + } + } + /** * If the theme directory has changed from before, clear the cache of all * ResourceBundles. @@ -153,6 +222,30 @@ public class I18n { } } + /** + * If we have a complete model access and the theme directory has changed + * from before, clear the cache of all ResourceBundles. + */ + private void checkForChangeInThemeDirectory(ServletContext ctx) { + WebappDaoFactory wdf = ModelAccess.on(ctx) + .getWebappDaoFactory(); + // Only applicable if context has a complete model access + if (Objects.nonNull(wdf) + && Objects.nonNull(wdf.getApplicationDao()) + && Objects.nonNull(wdf.getApplicationDao().getApplicationBean())) { + String currentDir = wdf + .getApplicationDao() + .getApplicationBean() + .getThemeDir(); + String previousDir = themeDirectory.getAndSet(currentDir); + if (!currentDir.equals(previousDir)) { + log.debug("Theme directory changed from '" + previousDir + "' to '" + + currentDir + "' - clearing the cache."); + ResourceBundle.clearCache(); + } + } + } + /** Only clear the cache one time per request. */ private void clearCacheOnRequest(HttpServletRequest req) { if (req.getAttribute(ATTRIBUTE_CACHE_CLEARED) != null) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java index 0d57cb8fd..c2060d99b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java @@ -10,6 +10,9 @@ import java.util.ResourceBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; + /** * A wrapper for a ResourceBundle that will not throw an exception, no matter * what string you request. @@ -20,7 +23,9 @@ import org.apache.commons.logging.LogFactory; */ public class I18nBundle { private static final Log log = LogFactory.getLog(I18nBundle.class); - + private static final String startSep = "\u25a4"; + private static final String endSep = "\u25a5"; + private static final String intSep = "\u25a6"; private static final String MESSAGE_BUNDLE_NOT_FOUND = "Text bundle ''{0}'' not found."; private static final String MESSAGE_KEY_NOT_FOUND = "Text bundle ''{0}'' has no text for ''{1}''"; @@ -75,13 +80,26 @@ public class I18nBundle { key); log.warn(message); textString = "ERROR: " + message; - } - String result = formatString(textString, parameters); + } + String message = formatString(textString, parameters); if (i18nLogger != null) { - i18nLogger.log(bundleName, key, parameters, textString, result); + i18nLogger.log(bundleName, key, parameters, textString, message); } - return result; + if (isNeedExportInfo()) { + String separatedArgs = ""; + for (int i = 0; i < parameters.length; i++) { + separatedArgs += parameters[i] + intSep; + } + return startSep + key + intSep + textString + intSep + separatedArgs + message + endSep; + } else { + return message; + } + + } + + private static boolean isNeedExportInfo() { + return DeveloperSettings.getInstance().getBoolean(Key.I18N_ONLINE_TRANSLATION); } private static String formatString(String textString, Object... parameters) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java index 3fd949e87..d0bff6349 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java @@ -89,8 +89,8 @@ public class LocaleSelectionDataGetter implements DataGetter { Locale currentLocale) throws FileNotFoundException { Map map = new HashMap<>(); map.put("code", locale.toString()); - map.put("label", locale.getDisplayName(currentLocale)); - map.put("imageUrl", LocaleSelectorUtilities.getImageUrl(vreq, locale)); + map.put("label", locale.getDisplayLanguage(locale)); + map.put("country", locale.getDisplayCountry(locale)); map.put("selected", currentLocale.equals(locale)); return map; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java index 6530d8aff..596880edd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java @@ -14,6 +14,7 @@ import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; /** @@ -48,6 +49,10 @@ public class LocaleSelectionSetup implements ServletContextListener { ss = StartupStatus.getBean(ctx); props = ConfigurationProperties.getBean(sce); + // Instantiate I18n instance to afford access to ServletContext + // when requesting a bundle with or without a VitroRequest + I18n.setup(ctx); + readProperties(); if (isForcing() && hasSelectables()) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java deleted file mode 100644 index 111181574..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java +++ /dev/null @@ -1,58 +0,0 @@ -/* $This file is distributed under the terms of the license in LICENSE$ */ - -package edu.cornell.mannlib.vitro.webapp.i18n.selection; - -import java.io.FileNotFoundException; -import java.util.Locale; -import java.util.Set; - -import javax.servlet.ServletContext; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; - -/** - * Some static methods for the GUI aspects of selecting a Locale. - */ -public class LocaleSelectorUtilities { - private static final Log log = LogFactory - .getLog(LocaleSelectorUtilities.class); - - /** - * Look in the current theme directory to find a selection image for this - * Locale. - * - * Images are expected at a resource path like - * /[themeDir]/i18n/images/select_locale_[locale_code].* - * - * For example, /themes/wilma/i18n/images/select_locale_en.png - * /themes/wilma/i18n/images/select_locale_en.JPEG - * /themes/wilma/i18n/images/select_locale_en.gif - * - * To create a proper URL, prepend the context path. - */ - public static String getImageUrl(VitroRequest vreq, Locale locale) - throws FileNotFoundException { - String filename = "select_locale_" + locale + "."; - - String themeDir = vreq.getAppBean().getThemeDir(); - String imageDirPath = "/" + themeDir + "i18n/images/"; - - ServletContext ctx = vreq.getSession().getServletContext(); - @SuppressWarnings("unchecked") - Set resourcePaths = ctx.getResourcePaths(imageDirPath); - if (resourcePaths != null) { - for (String resourcePath : resourcePaths) { - if (resourcePath.contains(filename)) { - String fullPath = vreq.getContextPath() + resourcePath; - log.debug("Found image for " + locale + " at '" + fullPath - + "'"); - return fullPath; - } - } - } - throw new FileNotFoundException("Can't find an image for " + locale); - } -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java index 4605436a4..f044546e0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.Optional; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -61,13 +63,10 @@ public abstract class SelectedLocale { ServletContext ctx = session.getServletContext(); Object ctxInfo = ctx.getAttribute(ATTRIBUTE_NAME); - if (ctxInfo instanceof ContextSelectedLocale) { - Locale forcedLocale = ((ContextSelectedLocale) ctxInfo) - .getForcedLocale(); - if (forcedLocale != null) { - log.debug("Found forced locale in the context: " + forcedLocale); - return forcedLocale; - } + + Optional forcedLocale = getForcedLocale(ctxInfo); + if (forcedLocale.isPresent()) { + return forcedLocale.get(); } Object sessionInfo = session.getAttribute(ATTRIBUTE_NAME); @@ -92,7 +91,62 @@ public abstract class SelectedLocale { } } - return null; + Locale fallbackLocale = getFallbackLocale(); + log.debug("Using fallback locale as default: " + fallbackLocale); + return fallbackLocale; + } + + /** + * Get the overriding Locale to use, which is the first of these to be found: + *
    + *
  • The forced Locale in the servlet context
  • + *
  • The first selectable locale matching a preferred locale
  • + *
  • The first of the preferred locale
  • + *
  • null
  • + *
+ */ + public static Locale getOverridingLocale(ServletContext ctx, List preferredLocales) { + Object ctxInfo = ctx.getAttribute(ATTRIBUTE_NAME); + Optional forcedLocale = getForcedLocale(ctxInfo); + if (forcedLocale.isPresent()) { + return forcedLocale.get(); + } + + if (ctxInfo instanceof ContextSelectedLocale) { + List selectableLocales = ((ContextSelectedLocale) ctxInfo) + .getSelectableLocales(); + + if (Objects.nonNull(selectableLocales) && Objects.nonNull(preferredLocales)) { + for (Locale preferredLocal : preferredLocales) { + for (Locale selectableLocale : selectableLocales) { + if (selectableLocale.equals(preferredLocal)) { + log.debug("Using first matching selectable locale from context: " + + selectableLocale); + return selectableLocale; + } + } + } + } + } + + if (Objects.nonNull(preferredLocales) && !preferredLocales.isEmpty()) { + Locale preferredLocal = preferredLocales.get(0); + log.debug("Using first preferred locale as default: " + + preferredLocal); + return preferredLocal; + } + + Locale fallbackLocale = getFallbackLocale(); + log.debug("Using fallback locale as default: " + fallbackLocale); + return fallbackLocale; + } + + /** + * @return a default locale to use if no other criteria for selecting a + * different one exist. + */ + public static Locale getFallbackLocale() { + return new Locale("en", "US"); } /** @@ -121,6 +175,42 @@ public abstract class SelectedLocale { return Locale.getDefault(); } + /** + * Get the preferred Locale to use, which is the first of these to be found: + *
    + *
  • The forced Locale in the servlet context
  • + *
  • The first selectable locale matching a preferred locale
  • + *
  • The first of the preferred locale
  • + *
  • The default Locale for the JVM
  • + *
+ */ + public static Locale getPreferredLocale(ServletContext ctx, List preferredLocales) { + Locale overridingLocale = getOverridingLocale(ctx, preferredLocales); + + if (overridingLocale != null) { + return overridingLocale; + } + + log.debug("Using default locale: " + Locale.getDefault()); + return Locale.getDefault(); + } + + /** + * Check for forced locale on the context. + */ + private static Optional getForcedLocale(Object ctxInfo) { + if (ctxInfo instanceof ContextSelectedLocale) { + Locale forcedLocale = ((ContextSelectedLocale) ctxInfo) + .getForcedLocale(); + if (forcedLocale != null) { + log.debug("Found forced locale in the context: " + forcedLocale); + return Optional.of(forcedLocale); + } + } + + return Optional.empty(); + } + /** * Store a list of selectable Locales in the servlet context, so we can * easily build the selection panel in the GUI. Clears any forced locale. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modelaccess/ModelNames.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modelaccess/ModelNames.java index 245ca9908..10493a0c3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modelaccess/ModelNames.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modelaccess/ModelNames.java @@ -13,20 +13,27 @@ public class ModelNames { public static final String ABOX_ASSERTIONS = "http://vitro.mannlib.cornell.edu/default/vitro-kb-2"; public static final String ABOX_INFERENCES = "http://vitro.mannlib.cornell.edu/default/vitro-kb-inf"; public static final String ABOX_UNION = "vitro:aboxOntModel"; + public static final String ABOX_ASSERTIONS_FIRSTTIME_BACKUP = ABOX_ASSERTIONS + "FirsttimeBackup"; public static final String TBOX_ASSERTIONS = "http://vitro.mannlib.cornell.edu/default/asserted-tbox"; public static final String TBOX_INFERENCES = "http://vitro.mannlib.cornell.edu/default/inferred-tbox"; public static final String TBOX_UNION = "vitro:tboxOntModel"; + public static final String TBOX_ASSERTIONS_FIRSTTIME_BACKUP = TBOX_ASSERTIONS + "FirsttimeBackup"; public static final String FULL_ASSERTIONS = "vitro:baseOntModel"; public static final String FULL_INFERENCES = "vitro:inferenceOntModel"; public static final String FULL_UNION = "vitro:jenaOntModel"; public static final String APPLICATION_METADATA = "http://vitro.mannlib.cornell.edu/default/vitro-kb-applicationMetadata"; + public static final String APPLICATION_METADATA_FIRSTTIME_BACKUP = APPLICATION_METADATA + "FirsttimeBackup"; public static final String USER_ACCOUNTS = "http://vitro.mannlib.cornell.edu/default/vitro-kb-userAccounts"; + public static final String USER_ACCOUNTS_FIRSTTIME_BACKUP = USER_ACCOUNTS + "FirsttimeBackup"; public static final String DISPLAY = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata"; + public static final String DISPLAY_FIRSTTIME_BACKUP = DISPLAY + "FirsttimeBackup"; public static final String DISPLAY_TBOX = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadataTBOX"; + public static final String DISPLAY_TBOX_FIRSTTIME_BACKUP = DISPLAY_TBOX + "FirsttimeBackup"; public static final String DISPLAY_DISPLAY = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata-displayModel"; + public static final String DISPLAY_DISPLAY_FIRSTTIME_BACKUP = DISPLAY_DISPLAY + "FirsttimeBackup"; /** * A map of the URIS, keyed by their short names, intended only for display @@ -39,17 +46,24 @@ public class ModelNames { map.put("ABOX_ASSERTIONS", ABOX_ASSERTIONS); map.put("ABOX_INFERENCES", ABOX_INFERENCES); map.put("ABOX_UNION", ABOX_UNION); + map.put("ABOX_ASSERTIONS_FIRSTTIME_BACKUP", ABOX_ASSERTIONS_FIRSTTIME_BACKUP); map.put("TBOX_ASSERTIONS", TBOX_ASSERTIONS); map.put("TBOX_INFERENCES", TBOX_INFERENCES); map.put("TBOX_UNION", TBOX_UNION); + map.put("TBOX_ASSERTIONS_FIRSTTIME_BACKUP", TBOX_ASSERTIONS_FIRSTTIME_BACKUP); map.put("FULL_ASSERTIONS", FULL_ASSERTIONS); map.put("FULL_INFERENCES", FULL_INFERENCES); map.put("FULL_UNION", FULL_UNION); map.put("APPLICATION_METADATA", APPLICATION_METADATA); + map.put("APPLICATION_METADATA_FIRSTTIME_BACKUP", APPLICATION_METADATA_FIRSTTIME_BACKUP); map.put("USER_ACCOUNTS", USER_ACCOUNTS); + map.put("USER_ACCOUNTS_FIRSTTIME_BACKUP", USER_ACCOUNTS_FIRSTTIME_BACKUP); map.put("DISPLAY", DISPLAY); + map.put("DISPLAY_FIRSTTIME_BACKUP", DISPLAY_FIRSTTIME_BACKUP); map.put("DISPLAY_TBOX", DISPLAY_TBOX); + map.put("DISPLAY_TBOX_FIRSTTIME_BACKUP", DISPLAY_TBOX_FIRSTTIME_BACKUP); map.put("DISPLAY_DISPLAY", DISPLAY_DISPLAY); + map.put("DISPLAY_DISPLAY_FIRSTTIME_BACKUP", DISPLAY_DISPLAY_FIRSTTIME_BACKUP); return Collections.unmodifiableMap(map); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modules/tripleSource/ConfigurationTripleSource.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modules/tripleSource/ConfigurationTripleSource.java index fa1725af5..fe766329b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modules/tripleSource/ConfigurationTripleSource.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/modules/tripleSource/ConfigurationTripleSource.java @@ -6,6 +6,14 @@ import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_DISPLAY; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_TBOX; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.USER_ACCOUNTS; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.ABOX_ASSERTIONS_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_ASSERTIONS_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.APPLICATION_METADATA_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.USER_ACCOUNTS_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_TBOX_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_DISPLAY_FIRSTTIME_BACKUP; + import org.apache.jena.rdf.model.ModelMaker; @@ -18,7 +26,10 @@ public abstract class ConfigurationTripleSource implements TripleSource { * add memory-mapping. */ protected static final String[] CONFIGURATION_MODELS = { DISPLAY, - DISPLAY_TBOX, DISPLAY_DISPLAY, USER_ACCOUNTS }; + DISPLAY_TBOX, DISPLAY_DISPLAY, USER_ACCOUNTS, ABOX_ASSERTIONS_FIRSTTIME_BACKUP, + TBOX_ASSERTIONS_FIRSTTIME_BACKUP, APPLICATION_METADATA_FIRSTTIME_BACKUP, + USER_ACCOUNTS_FIRSTTIME_BACKUP, DISPLAY_FIRSTTIME_BACKUP, + DISPLAY_TBOX_FIRSTTIME_BACKUP, DISPLAY_DISPLAY_FIRSTTIME_BACKUP }; /** * These decorators are added to a Configuration ModelMaker, regardless of diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java index babac6c52..06a17e4f4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java @@ -10,6 +10,8 @@ import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; import org.apache.jena.rdf.model.RDFNode; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; + /** * Interface for API to write, read, and update Vitro's RDF store, with support * to allow listening, logging and auditing. @@ -263,4 +265,10 @@ public interface RDFService { * multiple invocations do not cause an error. */ public void close(); + /** + * UQAM-Bug-Correction Useful among other things to transport the linguistic context in the service + * @param vitroRequest + */ + public void setVitroRequest(VitroRequest vitroRequest); + public VitroRequest getVitroRequest(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractGraphDecorator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractGraphDecorator.java new file mode 100644 index 000000000..c2f9faa67 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractGraphDecorator.java @@ -0,0 +1,136 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.adapters; + +import org.apache.jena.graph.Capabilities; +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.GraphEventManager; +import org.apache.jena.graph.GraphStatisticsHandler; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.TransactionHandler; +import org.apache.jena.graph.Triple; +import org.apache.jena.shared.AddDeniedException; +import org.apache.jena.shared.DeleteDeniedException; +import org.apache.jena.shared.PrefixMapping; +import org.apache.jena.util.iterator.ExtendedIterator; + +import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; + +/** + * The base class for a delegating graph decorator. + * + * As implemented, all methods simply delegate to the inner graph. Subclasses + * should override selected methods to provide functionality. + */ +public abstract class AbstractGraphDecorator implements + Graph { + + private final Graph inner; + + protected AbstractGraphDecorator(Graph g) { + if (g == null) { + throw new IllegalArgumentException("g may not be null."); + } + this.inner = g; + } + + @Override + public String toString() { + return ToString.simpleName(this) + "[" + ToString.hashHex(this) + + ", inner=" + ToString.graphToString(inner) + "]"; + } + + @Override + public void add(Triple arg0) throws AddDeniedException { + inner.add(arg0); + } + + @Override + public void clear() { + inner.clear(); + } + + @Override + public void close() { + inner.close(); + } + + @Override + public boolean contains(Triple arg0) { + return inner.contains(arg0); + } + + @Override + public boolean contains(Node arg0, Node arg1, Node arg2) { + return inner.contains(arg0, arg1, arg2); + } + + @Override + public void delete(Triple arg0) throws DeleteDeniedException { + inner.delete(arg0); + } + + @Override + public boolean dependsOn(Graph arg0) { + return inner.dependsOn(arg0); + } + + @Override + public ExtendedIterator find(Triple arg0) { + return inner.find(arg0); + } + + @Override + public ExtendedIterator find(Node arg0, Node arg1, Node arg2) { + return inner.find(arg0, arg1, arg2); + } + + @Override + public Capabilities getCapabilities() { + return inner.getCapabilities(); + } + + @Override + public GraphEventManager getEventManager() { + return inner.getEventManager(); + } + + @Override + public PrefixMapping getPrefixMapping() { + return inner.getPrefixMapping(); + } + + @Override + public GraphStatisticsHandler getStatisticsHandler() { + return inner.getStatisticsHandler(); + } + + @Override + public TransactionHandler getTransactionHandler() { + return inner.getTransactionHandler(); + } + + @Override + public boolean isClosed() { + return inner.isClosed(); + } + + @Override + public boolean isEmpty() { + return inner.isEmpty(); + } + + @Override + public boolean isIsomorphicWith(Graph arg0) { + return inner.isIsomorphicWith(arg0); + } + + @Override + public void remove(Node arg0, Node arg1, Node arg2) { + inner.remove(arg0, arg1, arg2); + } + + @Override + public int size() { + return inner.size(); + } + +} \ No newline at end of file diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java index 052d1b8db..6a5fc0f90 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java @@ -54,7 +54,7 @@ public abstract class AbstractModelDecorator implements Model { protected AbstractModelDecorator(Model m) { if (m == null) { - throw new NullPointerException("m may not be null."); + throw new IllegalArgumentException("m may not be null."); } this.inner = m; } @@ -325,6 +325,11 @@ public abstract class AbstractModelDecorator implements Model { return inner.lock(); } + @Override + public boolean hasNoMappings() { + return inner.hasNoMappings(); + } + @Override public boolean samePrefixMappingAs(PrefixMapping other) { return inner.samePrefixMappingAs(other); @@ -684,6 +689,11 @@ public abstract class AbstractModelDecorator implements Model { return inner.createResource(uri); } + @Override + public Resource createResource(Statement statement) { + return inner.createResource(statement); + } + @Override public Property createProperty(String nameSpace, String localName) { return inner.createProperty(nameSpace, localName); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java index 895928987..f9305d8f4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java @@ -364,6 +364,11 @@ public abstract class AbstractOntModelDecorator implements OntModel { return inner.lock(); } + @Override + public boolean hasNoMappings() { + return inner.hasNoMappings(); + } + @Override public boolean samePrefixMappingAs(PrefixMapping other) { return inner.samePrefixMappingAs(other); @@ -729,6 +734,11 @@ public abstract class AbstractOntModelDecorator implements OntModel { return inner.createResource(uri); } + @Override + public Resource createResource(Statement statement) { + return inner.createResource(statement); + } + @Override public Property createProperty(String nameSpace, String localName) { return inner.createProperty(nameSpace, localName); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/AcceptableLanguages.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/AcceptableLanguages.java new file mode 100644 index 000000000..01e96c9cc --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/AcceptableLanguages.java @@ -0,0 +1,36 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.filter; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A normalized list of languages/locales acceptable in results + * returned by language-filtering RDFServices, graphs, models, etc. + */ +public class AcceptableLanguages extends ArrayList{ + + private static final long serialVersionUID = 1L; + private static final Log log = LogFactory.getLog(AcceptableLanguages.class); + + /** + * Construct a normalized list of acceptable language strings + * from a set of raw language strings. For any values of form 'aa-BB', + * the base language ('aa') will be also added to the list. + * @param rawLanguageStrs may not be null + */ + public AcceptableLanguages(List rawLanguageStrs) { + log.debug("Raw language strings:" + rawLanguageStrs); + for (String lang : rawLanguageStrs) { + this.add(lang); + String baseLang = lang.split("-")[0]; + if (!lang.equals(baseLang) && !this.contains(baseLang)) { + this.add(baseLang); + } + } + log.debug("Normalized language strings:" + this); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/FilteredResultSet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/FilteredResultSet.java index 230766d3e..e116caece 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/FilteredResultSet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/FilteredResultSet.java @@ -9,6 +9,7 @@ import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; import org.apache.jena.rdf.model.Model; import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.engine.binding.BindingUtils; public class FilteredResultSet implements ResultSet { @@ -53,7 +54,7 @@ public class FilteredResultSet implements ResultSet { @Override public Binding nextBinding() { - throw new UnsupportedOperationException("Can we ignore this?"); + return BindingUtils.asBinding(nextSolution()); } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LangSort.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LangSort.java new file mode 100644 index 000000000..10007b119 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LangSort.java @@ -0,0 +1,78 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.filter; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A class for sorting language strings by acceptability according to a + * supplied list of language preferences. + * Refactored from LanguageFilteringRDFService for reuse by classes performing + * similar functions. + */ +public class LangSort { + + private static final Log log = LogFactory.getLog(LangSort.class); + + protected List langs; + private int inexactMatchPenalty; + private int noLanguage; + private int noMatch; + + /** + * Construct a language string sorter with a supplied list of preferred + * language strings + * @param preferredLanguageStrings list of preferred languages of form + * 'en-US', 'es', 'fr-CA'. May not be null. + */ + public LangSort(List preferredLanguageStrings) { + this.langs = preferredLanguageStrings; + this.inexactMatchPenalty = langs.size(); + // no language is worse than any inexact match (if the preferred list does not include ""). + this.noLanguage = 2 * inexactMatchPenalty; + // no match is worse than no language. + this.noMatch = noLanguage + 1; + } + + protected int compareLangs(String t1lang, String t2lang) { + int index1 = languageIndex(t1lang); + int index2 = languageIndex(t2lang); + if(index1 == index2) { + return t1lang.compareTo(t2lang); + } else { + return languageIndex(t1lang) - languageIndex(t2lang); + } + } + + /** + * Return index of exact match, or index of partial match, or + * language-free, or no match. + */ + private int languageIndex(String lang) { + if (lang == null) { + lang = ""; + } + + int index = langs.indexOf(lang); + if (index >= 0) { + log.debug("languageIndex for '" + lang + "' is " + index); + return index; + } + + if (lang.length() > 2) { + index = langs.indexOf(lang.substring(0, 2)); + if (index >= 0) { + log.debug("languageIndex for '" + lang + "' is " + index + inexactMatchPenalty); + return index + inexactMatchPenalty; + } + } + + if (lang.isEmpty()) { + log.debug("languageIndex for '" + lang + "' is " + noLanguage); + return noLanguage; + } + + return noMatch; + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilterModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilterModel.java new file mode 100644 index 000000000..0b1e5ce2f --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilterModel.java @@ -0,0 +1,104 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.filter; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; + +/** + * A filter of literal statements from Models according to language preferences. + */ +public class LanguageFilterModel { + + private static final Log log = LogFactory.getLog(LanguageFilterModel.class); + + /** + * + * @param m the model to filter. May not be null. + * @param langs list of strings of type 'en-US'. May not be null. + * @return model with language-inappropriate literal statements filtered out. + */ + public Model filterModel(Model m, List langs) { + log.debug("filterModel"); + List retractions = new ArrayList(); + StmtIterator stmtIt = m.listStatements(); + while (stmtIt.hasNext()) { + Statement stmt = stmtIt.nextStatement(); + if (stmt.getObject().isLiteral()) { + List candidatesForRemoval = m.listStatements( + stmt.getSubject(), stmt.getPredicate(), (RDFNode) null).toList(); + if (candidatesForRemoval.size() == 1) { + continue; + } + candidatesForRemoval.sort(new StatementSortByLang(langs)); + log.debug("sorted statements: " + showSortedStatements(candidatesForRemoval)); + Iterator candIt = candidatesForRemoval.iterator(); + String langRegister = null; + boolean chuckRemaining = false; + while(candIt.hasNext()) { + Statement s = candIt.next(); + if (!s.getObject().isLiteral()) { + continue; + } else if (chuckRemaining) { + retractions.add(s); + } + String lang = s.getObject().asLiteral().getLanguage(); + if (langRegister == null) { + langRegister = lang; + } else if (!langRegister.equals(lang)) { + chuckRemaining = true; + retractions.add(s); + } + } + } + + } + m.remove(retractions); + return m; + } + + private String showSortedStatements(List candidatesForRemoval) { + List langStrings = new ArrayList(); + for (Statement stmt: candidatesForRemoval) { + if (stmt == null) { + langStrings.add("null stmt"); + } else { + RDFNode node = stmt.getObject(); + if (!node.isLiteral()) { + langStrings.add("not literal"); + } else { + langStrings.add(node.asLiteral().getLanguage()); + } + } + } + return langStrings.toString(); + } + + private class StatementSortByLang extends LangSort implements Comparator { + + public StatementSortByLang(List langs) { + super(langs); + } + + public int compare(Statement s1, Statement s2) { + if (s1 == null || s2 == null) { + return 0; + } else if (!s1.getObject().isLiteral() || !s2.getObject().isLiteral()) { + return 0; + } + + String s1lang = s1.getObject().asLiteral().getLanguage(); + String s2lang = s2.getObject().asLiteral().getLanguage(); + + return compareLangs(s1lang, s2lang); + } + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringGraph.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringGraph.java new file mode 100644 index 000000000..b578fddeb --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringGraph.java @@ -0,0 +1,59 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.filter; + +import java.util.List; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.Triple; +import org.apache.jena.mem.GraphMem; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.util.iterator.ExtendedIterator; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.AbstractGraphDecorator; + +/** + * A graph decorator that filters find() results according to a list of + * preferred language strings + */ +public class LanguageFilteringGraph extends AbstractGraphDecorator + implements Graph { + + private List langs; + private LanguageFilterModel filterModel = new LanguageFilterModel(); + + /** + * Return a graph wrapped in a decorator that will filter find() results + * according to the supplied list of acceptable languages + * @param g the graph to wrap with language awareness. May not be null. + * @param preferredLanguages a list of preferred language strings. May not + * be null. + */ + protected LanguageFilteringGraph(Graph g, List preferredLanguages) { + super(g); + this.langs = preferredLanguages; + } + + @Override + public ExtendedIterator find(Triple arg0) { + return filter(super.find(arg0)); + + } + + @Override + public ExtendedIterator find(Node arg0, Node arg1, Node arg2) { + return filter(super.find(arg0, arg1, arg2)); + } + + private ExtendedIterator filter(ExtendedIterator triples) { + Graph tmp = new GraphMem(); + while(triples.hasNext()) { + Triple t = triples.next(); + tmp.add(t); + } + Model filteredModel = filterModel.filterModel( + ModelFactory.createModelForGraph(tmp), langs); + return filteredModel.getGraph().find(); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java index 903711e52..5e59118dd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java @@ -7,14 +7,14 @@ import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; import org.apache.jena.query.ResultSetFactory; @@ -22,11 +22,9 @@ import org.apache.jena.query.ResultSetFormatter; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.RDFNode; -import org.apache.jena.rdf.model.Statement; -import org.apache.jena.rdf.model.StmtIterator; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; @@ -39,27 +37,13 @@ public class LanguageFilteringRDFService implements RDFService { private static final Log log = LogFactory.getLog(LanguageFilteringRDFService.class); private RDFService s; private List langs; + private LanguageFilterModel filterModel = new LanguageFilterModel(); public LanguageFilteringRDFService(RDFService service, List langs) { this.s = service; - this.langs = normalizeLangs(langs); + this.langs = new AcceptableLanguages(langs); } - private List normalizeLangs(List langs) { - log.debug("Preferred languages:" + langs); - - List normalizedLangs = new ArrayList(langs); - for (String lang : langs) { - String baseLang = lang.split("-")[0]; - if (!normalizedLangs.contains(baseLang)) { - normalizedLangs.add(baseLang); - } - } - - log.debug("Normalized languages:" + normalizedLangs); - return normalizedLangs; - } - @Override public boolean changeSetUpdate(ChangeSet changeSet) throws RDFServiceException { @@ -83,23 +67,17 @@ public class LanguageFilteringRDFService implements RDFService { public InputStream sparqlConstructQuery(String query, ModelSerializationFormat resultFormat) throws RDFServiceException { - Model m = RDFServiceUtils.parseModel(s.sparqlConstructQuery(query, resultFormat), resultFormat); - InputStream in = outputModel(filterModel(m), resultFormat); + Model m = RDFServiceUtils.parseModel(s.sparqlConstructQuery( + query, resultFormat), resultFormat); + InputStream in = outputModel(filterModel.filterModel( + m, langs), resultFormat); return in; } @Override public void sparqlConstructQuery(String query, Model model) throws RDFServiceException { - if (model.isEmpty()) { - s.sparqlConstructQuery(query, model); - filterModel(model); - } else { - Model constructedModel = ModelFactory.createDefaultModel(); - s.sparqlConstructQuery(query, constructedModel); - filterModel(constructedModel); - model.add(constructedModel); - } + s.sparqlConstructQuery(query, model); } @Override @@ -107,7 +85,7 @@ public class LanguageFilteringRDFService implements RDFService { ModelSerializationFormat resultFormat) throws RDFServiceException { Model m = RDFServiceUtils.parseModel(s.sparqlDescribeQuery(query, resultFormat), resultFormat); - return outputModel(filterModel(m), resultFormat); + return outputModel(filterModel.filterModel(m, langs), resultFormat); } private InputStream outputModel(Model m, ModelSerializationFormat resultFormat) { @@ -116,62 +94,6 @@ public class LanguageFilteringRDFService implements RDFService { return new ByteArrayInputStream(out.toByteArray()); } - private Model filterModel(Model m) { - log.debug("filterModel"); - List retractions = new ArrayList(); - StmtIterator stmtIt = m.listStatements(); - while (stmtIt.hasNext()) { - Statement stmt = stmtIt.nextStatement(); - if (stmt.getObject().isLiteral()) { - List candidatesForRemoval = m.listStatements( - stmt.getSubject(), stmt.getPredicate(), (RDFNode) null).toList(); - if (candidatesForRemoval.size() == 1) { - continue; - } - candidatesForRemoval.sort(new StatementSortByLang()); - log.debug("sorted statements: " + showSortedStatements(candidatesForRemoval)); - Iterator candIt = candidatesForRemoval.iterator(); - String langRegister = null; - boolean chuckRemaining = false; - while(candIt.hasNext()) { - Statement s = candIt.next(); - if (!s.getObject().isLiteral()) { - continue; - } else if (chuckRemaining) { - retractions.add(s); - } - String lang = s.getObject().asLiteral().getLanguage(); - if (langRegister == null) { - langRegister = lang; - } else if (!langRegister.equals(lang)) { - chuckRemaining = true; - retractions.add(s); - } - } - } - - } - m.remove(retractions); - return m; - } - - private String showSortedStatements(List candidatesForRemoval) { - List langStrings = new ArrayList(); - for (Statement stmt: candidatesForRemoval) { - if (stmt == null) { - langStrings.add("null stmt"); - } else { - RDFNode node = stmt.getObject(); - if (!node.isLiteral()) { - langStrings.add("not literal"); - } else { - langStrings.add(node.asLiteral().getLanguage()); - } - } - } - return langStrings.toString(); - } - @Override public InputStream sparqlSelectQuery(String query, ResultFormat resultFormat) throws RDFServiceException { @@ -180,7 +102,16 @@ public class LanguageFilteringRDFService implements RDFService { s.sparqlSelectQuery(query, RDFService.ResultFormat.JSON)); List solnList = getSolutionList(resultSet); List vars = resultSet.getResultVars(); + + // This block loops all of the Query variables; + // for each QuerySolution, creates a map of the values of the other variables than the current + // 'variable' --> a list of RowIndexedLiterals. + // In this way, all of the QuerySolutions with equal values of their other variables are grouped. + // This map is used subsequently to filter Literals based on lang for (String var : vars) { + Map, List> nonVarToRowIndexedLiterals = new HashMap<>(); + + // First pass of solnList to populate map for (int i = 0; i < solnList.size(); i++) { QuerySolution s = solnList.get(i); if (s == null) { @@ -190,23 +121,32 @@ public class LanguageFilteringRDFService implements RDFService { if (node == null || !node.isLiteral()) { continue; } - List candidatesForRemoval = - new ArrayList(); - candidatesForRemoval.add(new RowIndexedLiteral(node.asLiteral(), i)); - for (int j = i + 1; j < solnList.size(); j++) { - QuerySolution t = solnList.get(j); - if (t == null) { - continue; - } - if (matchesExceptForVar(s, t, var, vars)) { - candidatesForRemoval.add( - new RowIndexedLiteral(t.getLiteral(var), j)); + + // Create entry representing values other than current 'var' for this QuerySolution + List nonVarList = new ArrayList(vars.size() - 1); + for (String v : vars) { + if (!v.equals(var)) { + nonVarList.add(s.get(v)); } } + + List rowIndexedLiterals = nonVarToRowIndexedLiterals.get(nonVarList); + if (rowIndexedLiterals == null) { + rowIndexedLiterals = new ArrayList(); + } + rowIndexedLiterals.add(new RowIndexedLiteral(node.asLiteral(), i)); + + // Add RowIndexedLiterals to the map + nonVarToRowIndexedLiterals.put(nonVarList, rowIndexedLiterals); + } + + // Second pass of solnList (via the map) to evaluate candidatesForRemoval + for (List key : nonVarToRowIndexedLiterals.keySet()) { + List candidatesForRemoval = nonVarToRowIndexedLiterals.get(key); if (candidatesForRemoval.size() == 1) { continue; } - candidatesForRemoval.sort(new RowIndexedLiteralSortByLang()); + candidatesForRemoval.sort(new RowIndexedLiteralSortByLang(langs)); log.debug("sorted RowIndexedLiterals: " + showSortedRILs(candidatesForRemoval)); Iterator candIt = candidatesForRemoval.iterator(); String langRegister = null; @@ -254,10 +194,9 @@ public class LanguageFilteringRDFService implements RDFService { @Override public void sparqlSelectQuery(String query, ResultSetConsumer consumer) throws RDFServiceException { log.debug("sparqlSelectQuery: " + query.replaceAll("\\s+", " ")); - s.sparqlSelectQuery(query, new ResultSetConsumer.Chaining(consumer) { List vars; - List solnList = new ArrayList(); + List solnList = new ArrayList<>(); @Override protected void processQuerySolution(QuerySolution qs) { @@ -273,7 +212,15 @@ public class LanguageFilteringRDFService implements RDFService { protected void endProcessing() { chainStartProcessing(); + // This block loops all of the Query variables; + // for each QuerySolution, creates a map of the values of the other variables than the current + // 'variable' --> a list of RowIndexedLiterals. + // In this way, all of the QuerySolutions with equal values of their other variables are grouped. + // This map is used subsequently to filter Literals based on lang for (String var : vars) { + Map, List> nonVarToRowIndexedLiterals = new HashMap<>(); + + // First pass of solnList to populate map for (int i = 0; i < solnList.size(); i++) { QuerySolution s = solnList.get(i); if (s == null) { @@ -283,23 +230,32 @@ public class LanguageFilteringRDFService implements RDFService { if (node == null || !node.isLiteral()) { continue; } - List candidatesForRemoval = - new ArrayList(); - candidatesForRemoval.add(new RowIndexedLiteral(node.asLiteral(), i)); - for (int j = i + 1; j < solnList.size(); j++) { - QuerySolution t = solnList.get(j); - if (t == null) { - continue; - } - if (matchesExceptForVar(s, t, var, vars)) { - candidatesForRemoval.add( - new RowIndexedLiteral(t.getLiteral(var), j)); + + // Create entry representing values other than current 'var' for this QuerySolution + List nonVarList = new ArrayList(vars.size() - 1); + for (String v : vars) { + if (!v.equals(var)) { + nonVarList.add(s.get(v)); } } + + List rowIndexedLiterals = nonVarToRowIndexedLiterals.get(nonVarList); + if (rowIndexedLiterals == null) { + rowIndexedLiterals = new ArrayList(); + } + rowIndexedLiterals.add(new RowIndexedLiteral(node.asLiteral(), i)); + + // Add RowIndexedLiterals to the map + nonVarToRowIndexedLiterals.put(nonVarList, rowIndexedLiterals); + } + + // Second pass of solnList (via the map) to evaluate candidatesForRemoval + for (List key : nonVarToRowIndexedLiterals.keySet()) { + List candidatesForRemoval = nonVarToRowIndexedLiterals.get(key); if (candidatesForRemoval.size() == 1) { continue; } - candidatesForRemoval.sort(new RowIndexedLiteralSortByLang()); + candidatesForRemoval.sort(new RowIndexedLiteralSortByLang(langs)); log.debug("sorted RowIndexedLiterals: " + showSortedRILs(candidatesForRemoval)); Iterator candIt = candidatesForRemoval.iterator(); String langRegister = null; @@ -357,34 +313,6 @@ public class LanguageFilteringRDFService implements RDFService { } - private boolean matchesExceptForVar(QuerySolution a, QuerySolution b, - String varName, List varList) { - if (varName == null) { - throw new RuntimeException("expected non-null variable nane"); - } - for (String var : varList) { - RDFNode nodea = a.get(var); - RDFNode nodeb = b.get(var); - if (var.equals(varName)) { - if (nodea == null || !nodea.isLiteral() || nodeb == null || !nodeb.isLiteral()) { - return false; - } - } else { - if (nodea == null && nodeb == null) { - continue; - } else if (nodea == null && nodeb != null) { - return false; - } else if (nodeb == null && nodea != null) { - return false; - } - if (!a.get(var).equals(b.get(var))) { - return false; - } - } - } - return true; - } - private List getSolutionList(ResultSet resultSet) { List solnList = new ArrayList(); while (resultSet.hasNext()) { @@ -489,52 +417,12 @@ public class LanguageFilteringRDFService implements RDFService { s.close(); } - private class LangSort { - // any inexact match is worse than any exact match - private int inexactMatchPenalty = langs.size(); - // no language is worse than any inexact match (if the preferred list does not include ""). - private int noLanguage = 2 * inexactMatchPenalty; - // no match is worse than no language. - private int noMatch = noLanguage + 1; - - protected int compareLangs(String t1lang, String t2lang) { - return languageIndex(t1lang) - languageIndex(t2lang); - } - - /** - * Return index of exact match, or index of partial match, or - * language-free, or no match. - */ - private int languageIndex(String lang) { - if (lang == null) { - lang = ""; - } - - int index = langs.indexOf(lang); - if (index >= 0) { - log.debug("languageIndex for '" + lang + "' is " + index); - return index; - } - - if (lang.length() > 2) { - index = langs.indexOf(lang.substring(0, 2)); - if (index >= 0) { - log.debug("languageIndex for '" + lang + "' is " + index + inexactMatchPenalty); - return index + inexactMatchPenalty; - } - } - - if (lang.isEmpty()) { - log.debug("languageIndex for '" + lang + "' is " + noLanguage); - return noLanguage; - } - - return noMatch; - } - } - private class RowIndexedLiteralSortByLang extends LangSort implements Comparator { + public RowIndexedLiteralSortByLang(List langs) { + super(langs); + } + public int compare(RowIndexedLiteral rilit1, RowIndexedLiteral rilit2) { if (rilit1 == null || rilit2 == null) { return 0; @@ -547,21 +435,20 @@ public class LanguageFilteringRDFService implements RDFService { } } - private class StatementSortByLang extends LangSort implements Comparator { + /* + * UQAM-Linguistic-Management Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; - public int compare(Statement s1, Statement s2) { - if (s1 == null || s2 == null) { - return 0; - } else if (!s1.getObject().isLiteral() || !s2.getObject().isLiteral()) { - return 0; - } + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } - String s1lang = s1.getObject().asLiteral().getLanguage(); - String s2lang = s2.getObject().asLiteral().getLanguage(); - - return compareLangs(s1lang, s2lang); - } - } + public VitroRequest getVitroRequest() { + return vitroRequest; + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringUtils.java index 5a0336462..720bcc148 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringUtils.java @@ -4,26 +4,62 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.filter; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import javax.servlet.ServletRequest; +import org.apache.commons.lang3.LocaleUtils; import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.ModelFactory; -import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceGraph; -import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; - /** * Some methods that will come in handy when dealing with Language Filtering */ public class LanguageFilteringUtils { + private static final String UNDERSCORE = "_"; + private static final String HYPHEN = "-"; + private static final String DEFAULT_LANG_STRING = "en"; + + /** + * Take a Locale object, such as we might get from a + * request, and convert to a language string used in RDF. + * + * While converting, change all underscores (as in Locale names) to hyphens + * (as in RDF language specifiers). + */ + public static String localeToLanguage(Locale locale) { + return locale.toString().replace(UNDERSCORE, HYPHEN); + } + + /** + * Take a language string and convert to a Locale. + * + * While converting, change all hyphens (as in RDF language specifiers) to + * underscores (as in Locale names). Ensure language string is lowercase + * and country abbreviation is uppercase. + */ + public static Locale languageToLocale(String langStr) { + String[] langParts = langStr.split(HYPHEN); + if (langParts.length > 2) { + langStr = String.join(UNDERSCORE, langParts[0].toLowerCase(), + langParts[1].toUpperCase(), langParts[2]); + } else if (langParts.length > 1) { + langStr = String.join(UNDERSCORE, langParts[0].toLowerCase(), + langParts[1].toUpperCase()); + } else { + langStr = langParts[0].toLowerCase(); + } + return LocaleUtils.toLocale(langStr); + } + /** * Take an Enumeration of Locale objects, such as we might get from a - * request, and convert to a List of langauage strings, such as are needed + * request, and convert to a List of language strings, such as are needed * by the LanguageFilteringRDFService. * * While converting, change all underscores (as in Locale names) to hyphens @@ -33,27 +69,43 @@ public class LanguageFilteringUtils { List langs = new ArrayList<>(); while (locales.hasMoreElements()) { Locale locale = (Locale) locales.nextElement(); - langs.add(locale.toString().replace("_", "-")); + langs.add(locale.toString().replace(UNDERSCORE, HYPHEN)); } if (langs.isEmpty()) { - langs.add("en"); + langs.add(DEFAULT_LANG_STRING); } - return langs; + return langs; } /** - * Add a Language Filtering layer to an OntModel by treating it as an RDFService. + * Take a List of language strings and convert to a List of Locale. + * + * While converting, change all hyphens (as in RDF language specifiers) to + * under scores (as in Locale names). Ensure language string is lowercase + * and country abbreviation is uppercase. + */ + public static List languagesToLocales(List langs) { + Set locales = new HashSet<>(); + langs.forEach(langStr -> { + locales.add(languageToLocale(langStr)); + }); + if (locales.isEmpty()) { + locales.add(LocaleUtils.toLocale(DEFAULT_LANG_STRING)); + } + + return new ArrayList<>(locales); + } + + /** + * Add a Language Filtering layer to an OntModel */ public static OntModel wrapOntModelInALanguageFilter(OntModel rawModel, ServletRequest req) { - /** This is some nasty layering. Could we do this more easily? */ - List languages = localesToLanguages(req.getLocales()); + List languages = new AcceptableLanguages(localesToLanguages(req.getLocales())); return ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, - RDFServiceGraph.createRDFServiceModel( - new RDFServiceGraph( - new LanguageFilteringRDFService( - new RDFServiceModel(rawModel), languages)))); + ModelFactory.createModelForGraph(new LanguageFilteringGraph( + rawModel.getGraph(), languages))); } private LanguageFilteringUtils() { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java index f44d2b59f..44bd2aabb 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java @@ -9,6 +9,7 @@ import java.util.List; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; @@ -218,7 +219,20 @@ public class RDFServiceFactorySingle implements RDFServiceFactory { return ToString.simpleName(this) + "[" + ToString.hashHex(this) + ", inner=" + s + "]"; } + /* + * UQAM-Linguistic-Management Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } + + public VitroRequest getVitroRequest() { + return vitroRequest; + } } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java index 217149283..6ff961382 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.riot.out.NodeFormatterTTL; import org.apache.jena.vocabulary.RDF; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange; @@ -141,8 +142,8 @@ public abstract class RDFServiceImpl implements RDFService { } protected void notifyListeners(ModelChange modelChange) throws IOException { + modelChange.getSerializedModel().reset(); for (ChangeListener listener : registeredListeners) { - modelChange.getSerializedModel().reset(); listener.notifyModelChange(modelChange); } log.debug(registeredJenaListeners.size() + " registered Jena listeners"); @@ -254,11 +255,12 @@ public abstract class RDFServiceImpl implements RDFService { literalBuff.append("\""); pyString(literalBuff, node.getLiteralLexicalForm()); literalBuff.append("\""); - if (node.getLiteralDatatypeURI() != null) { - literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); - } else if (node.getLiteralLanguage() != null && node.getLiteralLanguage().length() > 0) { + if (node.getLiteralLanguage() != null && node.getLiteralLanguage().length() > 0) { literalBuff.append("@").append(node.getLiteralLanguage()); + } else if (node.getLiteralDatatypeURI() != null) { + literalBuff.append("^^<").append(node.getLiteralDatatypeURI()).append(">"); } + return literalBuff.toString(); } else { return varName; @@ -453,4 +455,19 @@ public abstract class RDFServiceImpl implements RDFService { } } } + /* + * UQAM Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; + + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } + + public VitroRequest getVitroRequest() { + return vitroRequest; + } + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java index dadccfed4..09a98a647 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java @@ -42,6 +42,7 @@ import org.apache.jena.sdb.SDB; import org.apache.jena.shared.Lock; import org.apache.jena.sparql.core.Quad; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper; import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph; @@ -200,8 +201,8 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic log.debug("blank node model size " + blankNodeModel.size()); if (blankNodeModel.size() == 1) { - log.warn("Deleting single triple with blank node: " + blankNodeModel); - log.warn("This likely indicates a problem; excessive data may be deleted."); + log.debug("Deleting single triple with blank node: " + blankNodeModel); + log.debug("This could result in the deletion of multiple triples if multiple blank nodes match the same triple pattern."); } Query rootFinderQuery = QueryFactory.create(BNODE_ROOT_QUERY); @@ -704,4 +705,19 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic protected QueryExecution createQueryExecution(String queryString, Query q, Dataset d) { return QueryExecutionFactory.create(q, d); } + /* + * UQAM-Linguistic-Management Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; + + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } + + public VitroRequest getVitroRequest() { + return vitroRequest; + } + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/model/RDFServiceModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/model/RDFServiceModel.java index 496928fbc..4b910b568 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/model/RDFServiceModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/model/RDFServiceModel.java @@ -13,6 +13,7 @@ import org.apache.jena.query.Dataset; import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.Model; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange; @@ -119,4 +120,18 @@ public class RDFServiceModel extends RDFServiceJena implements RDFService { return true; } + /* + * UQAM-Linguistic-Management Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; + + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } + + public VitroRequest getVitroRequest() { + return vitroRequest; + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java index 6b5e4557a..adcb69826 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java @@ -13,6 +13,8 @@ import java.nio.file.Paths; import java.util.List; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; + import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; @@ -74,11 +76,19 @@ public class RDFServiceTDB extends RDFServiceJena { } notifyListenersOfPreChangeEvents(changeSet); - dataset.begin(ReadWrite.WRITE); - try { - applyChangeSetToModel(changeSet, dataset); - dataset.commit(); - } finally { + dataset.begin(ReadWrite.WRITE); + try { + boolean committed = false; + try { + applyChangeSetToModel(changeSet, dataset); + dataset.commit(); + committed = true; + } finally { + if(!committed) { + dataset.abort(); + } + } + } finally { dataset.end(); } @@ -93,6 +103,10 @@ public class RDFServiceTDB extends RDFServiceJena { } } + @Override + public boolean preferPreciseOptionals() { + return true; + } @Override public void close() { @@ -232,6 +246,28 @@ public class RDFServiceTDB extends RDFServiceJena { return isEquivalentGraph(graphURI, inStream, ModelSerializationFormat.NTRIPLE); } + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) + throws RDFServiceException { + dataset.begin(ReadWrite.READ); + try { + return super.countTriples(subject, predicate, object); + } finally { + dataset.end(); + } + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, + long limit, long offset) throws RDFServiceException { + dataset.begin(ReadWrite.READ); + try { + return super.getTriples(subject, predicate, object, limit, offset); + } finally { + dataset.end(); + } + } + /** * Convert all of the references to integer compatible type to "integer" in the serialized graph. * diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java index 5a0189f41..abae2aeb9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java @@ -9,6 +9,7 @@ import java.util.List; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; @@ -207,4 +208,19 @@ public class LoggingRDFService implements RDFService { public String toString() { return "LoggingRDFService[inner=" + innerService + "]"; } + /* + * UQAM-Linguistic-Management Useful among other things to transport the linguistic context in the service + * (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService#setVitroRequest(edu.cornell.mannlib.vitro.webapp.controller.VitroRequest) + */ + private VitroRequest vitroRequest; + + public void setVitroRequest(VitroRequest vitroRequest) { + this.vitroRequest = vitroRequest; + } + + public VitroRequest getVitroRequest() { + return vitroRequest; + } + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java index 6596878b2..578c6a4a6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectProperties.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectProperties.java index d8b2233fc..6462cd3fc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectProperties.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectProperties.java @@ -59,16 +59,16 @@ public class AdditionalURIsForObjectProperties implements IndexingUriFinder, Con public void endIndexing() { /* nothing to do */ } protected List doObjectPropertyStmt(Statement stmt) { - // Only need to consider the object since the subject - // will already be updated in search index as part of - // SearchReindexingListener. - // Also, context nodes are not handled here. They are // taken care of in AdditionalURIsForContextNodex. - if( stmt.getObject().isURIResource() ) - return Collections.singletonList( stmt.getObject().as(Resource.class).getURI() ); - else - return Collections.emptyList(); + List uris = new ArrayList(); + if( stmt.getObject().isURIResource() ) { + uris.add(stmt.getObject().as(Resource.class).getURI() ); + } + if( stmt.getSubject().isURIResource() ) { + uris.add(stmt.getSubject().as(Resource.class).getURI() ); + } + return uris; } protected List doDataPropertyStmt(Statement stmt) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java index 383177cd7..770aab181 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/FakeApplicationOntologyService.java @@ -445,14 +445,26 @@ public class FakeApplicationOntologyService { * "display model". The query finds a preferred title for the individual. */ private static class FakeVivoPeopleDataGetter extends SparqlQueryDataGetter { - private static String QUERY_STRING = "" + // private static String QUERY_STRING = "" + // + "PREFIX obo: \n" + // + "PREFIX vcard: \n" + // + "SELECT ?pt \n" + "WHERE { \n" + // + " ?uri obo:ARG_2000028 ?vIndividual . \n" + // + " ?vIndividual vcard:hasTitle ?vTitle . \n" + // + " ?vTitle vcard:title ?pt . \n" + "} LIMIT 1"; + + /* + * UQAM-Optimization New query including Linguistic context + */ + private static String QUERY_STRING_LANG = "" + "PREFIX obo: \n" + "PREFIX vcard: \n" + "SELECT ?pt \n" + "WHERE { \n" + " ?uri obo:ARG_2000028 ?vIndividual . \n" + " ?vIndividual vcard:hasTitle ?vTitle . \n" - + " ?vTitle vcard:title ?pt . \n" + "} LIMIT 1"; - + + " ?vTitle vcard:title ?pt . \n" + + " FILTER (lang(?pt) = '?langCtx' ) \n" + + " } LIMIT 1"; private static final String FAKE_VIVO_PEOPLE_DATA_GETTER_URI = "http://FakeVivoPeopleDataGetter"; private static OntModel fakeDisplayModel = initializeFakeDisplayModel(); @@ -467,7 +479,7 @@ public class FakeApplicationOntologyService { Property saveToVarProperty = m .getProperty(DisplayVocabulary.SAVE_TO_VAR); - m.add(dataGetter, queryProperty, QUERY_STRING); + m.add(dataGetter, queryProperty, QUERY_STRING_LANG); //UQAM-Optimization Using query with linguistic context m.add(dataGetter, saveToVarProperty, "extra"); return m; } @@ -475,18 +487,24 @@ public class FakeApplicationOntologyService { private String individualUri; private VitroRequest vreq; private ServletContext ctx; + private String langCtx = "en-US"; public FakeVivoPeopleDataGetter(VitroRequest vreq, String individualUri) { - super(vreq, fakeDisplayModel, "http://FakeVivoPeopleDataGetter"); + super(vreq, initializeFakeDisplayModel(), "http://FakeVivoPeopleDataGetter"); this.individualUri = individualUri; this.vreq = vreq; this.ctx = vreq.getSession().getServletContext(); + this.langCtx = vreq.getLocale().getLanguage(); // UQAM-Optimization add the linguistic context + if (!vreq.getLocale().getCountry().isEmpty()) { + this.langCtx += "-" + vreq.getLocale().getCountry(); + } } @Override public Map getData(Map pageData) { Map parms = new HashMap<>(); parms.put("uri", individualUri); + parms.put("langCtx", langCtx); //UQAM-Optimization add the linguistic context return super.getData(parms); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ConfigurationModelsSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ConfigurationModelsSetup.java index 1c0ea33ce..6c6429327 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ConfigurationModelsSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ConfigurationModelsSetup.java @@ -12,15 +12,30 @@ import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.jena.ontology.OntModel; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; +import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory; /** * Set up the models that use the CONFIGURATION RDFService. They are all mapped * to memory-based models. */ public class ConfigurationModelsSetup implements ServletContextListener { + private static final Log log = LogFactory.getLog(ConfigurationModelsSetup.class); + @Override public void contextInitialized(ServletContextEvent sce) { ServletContext ctx = sce.getServletContext(); @@ -31,33 +46,223 @@ public class ConfigurationModelsSetup implements ServletContextListener { setupModel(ctx, DISPLAY_TBOX, "displayTbox"); setupModel(ctx, DISPLAY_DISPLAY, "displayDisplay"); setupModel(ctx, USER_ACCOUNTS, "auth"); - ss.info(this, - "Set up the display models and the user accounts model."); + ss.info(this, "Set up the display models and the user accounts model."); } catch (Exception e) { ss.fatal(this, e.getMessage(), e.getCause()); } } - private void setupModel(ServletContext ctx, String modelUri, - String modelPath) { + private void setupModel(ServletContext ctx, String modelUri, String modelPath) { try { OntModel ontModel = ModelAccess.on(ctx).getOntModel(modelUri); - loadFirstTimeFiles(ctx, modelPath, ontModel); + if (ontModel.isEmpty()) { + loadFirstTimeFiles(ctx, modelPath, ontModel); + // backup firsttime files + OntModel baseModelFirsttime = ModelAccess.on(ctx).getOntModel(modelUri + "FirsttimeBackup"); + baseModelFirsttime.add(ontModel); + } else { + // Check if the firsttime files have changed since the firsttime startup, + // if so, then apply the changes but not overwrite the whole user model + applyFirstTimeChanges(ctx, modelPath, modelUri, ontModel); + } + loadEveryTimeFiles(ctx, modelPath, ontModel); } catch (Exception e) { - throw new RuntimeException("Failed to create the '" + modelPath - + "' model (" + modelUri + ").", e); + throw new RuntimeException("Failed to create the '" + modelPath + "' model (" + modelUri + ").", e); } } - private void loadFirstTimeFiles(ServletContext ctx, String modelPath, - OntModel baseModel) { - RDFFilesLoader.loadFirstTimeFiles(modelPath, baseModel, baseModel.isEmpty()); + private void loadFirstTimeFiles(ServletContext ctx, String modelPath, OntModel baseModel) { + RDFFilesLoader.loadFirstTimeFiles(ctx, modelPath, baseModel, baseModel.isEmpty()); } - private void loadEveryTimeFiles(ServletContext ctx, String modelPath, - OntModel memoryModel) { - RDFFilesLoader.loadEveryTimeFiles(modelPath, memoryModel); + private void loadEveryTimeFiles(ServletContext ctx, String modelPath, OntModel memoryModel) { + RDFFilesLoader.loadEveryTimeFiles(ctx, modelPath, memoryModel); + } + + /* + * Check if the firsttime files have changed since the firsttime startup, if so, + * then apply the changes but not overwrite the whole user model + */ + private void applyFirstTimeChanges(ServletContext ctx, String modelPath, String modelUri, OntModel userModel) { + + log.info("Reload firsttime files on start-up if changed: '" + modelPath +"', URI: '" + modelUri + "'"); + boolean updatedFiles = false; + + // get configuration models from the firsttime start up (backup state) + OntModel baseModelFirsttimeBackup = ModelAccess.on(ctx).getOntModel(modelUri + "FirsttimeBackup"); + + // compare firsttime files with configuration models + log.debug("compare firsttime files with configuration models (backup from first start) for " + modelPath); + + OntModel baseModelFirsttime = VitroModelFactory.createOntologyModel(); + RDFFilesLoader.loadFirstTimeFiles(ctx, modelPath, baseModelFirsttime, true); + + if (baseModelFirsttime.isIsomorphicWith(baseModelFirsttimeBackup)) { + log.debug("They are the same, so do nothing: '" + modelPath + "'"); + } else { + log.debug("They differ:" + modelPath + ", compare values in configuration models with user's triplestore"); + + updatedFiles = applyChanges(baseModelFirsttimeBackup, baseModelFirsttime, userModel, modelPath); + if (updatedFiles) + log.info("The model was updated, " + modelPath); + } + } + + /* + * This method is designed to compare configuration models (baseModel) with firsttime files (newModel): + * if they are the same, stop + * else, if they differ, compare values in configuration models (baseModel) with user's triplestore + * if they are the same, update user's triplestore with value in new firsttime files + * else, if they differ, leave user's triplestore statement alone + * finally, overwrite the configuration models with content of the updated firstime files + * + * @param baseModel The backup firsttime model (from the first startup) + * @param newModel The current state of the firsttime files in the directory + * @param userModel The current state of the user model + * @param modelIdString Just an string for the output for better debugging + * (display, displayTbox, displayDisplay, auth) + */ + private boolean applyChanges(Model baseModel, Model newModel, Model userModel, String modelIdString) { + boolean updatedFiles = false; + StringWriter out = new StringWriter(); + StringWriter out2 = new StringWriter(); + Model difOldNew = baseModel.difference(newModel); + Model difNewOld = newModel.difference(baseModel); + + // remove special cases for display, problem with blank nodes + if (modelIdString.equals("display")) { + + removeBlankTriples(difOldNew); + removeBlankTriples(difNewOld); + } + + if (difOldNew.isEmpty() && difNewOld.isEmpty()) { + // if there is no difference, nothing needs to be done + log.debug("For the " + modelIdString + " model, there is no difference in both directions. So do nothing."); + } else { + // if there is a difference, we need to remove the triples in difOldNew and + // add the triples in difNewOld to the back up firsttime model + + if (!difOldNew.isEmpty()) { + difOldNew.write(out, "TTL"); + log.debug("Difference for " + modelIdString + " (old -> new), these triples should be removed: " + out); + + // Check if the UI-changes Overlap with the changes made in the fristtime-files + checkUiChangesOverlapWithFileChanges(baseModel, userModel, difOldNew); + + // before we remove the triples, we need to compare values in back up firsttime with user's triplestore + // if the triples which should be removed are still in user´s triplestore, remove them + if (userModel.containsAny(difOldNew)) { + log.debug("Some of these triples are in the user triples store, so they will be removed now"); + userModel.remove(difOldNew); + updatedFiles = true; + } + + // remove the triples from the backup firsttime model for the next check + baseModel.remove(difOldNew); + } + if (!difNewOld.isEmpty()) { + difNewOld.write(out2, "TTL"); + log.debug("Difference for " + modelIdString + " (new -> old), these triples should be added: " + out2); + + // Check if the UI-changes Overlap with the changes made in the fristtime-files + checkUiChangesOverlapWithFileChanges(baseModel, userModel, difNewOld); + + // before we add the triples, we need to compare values in back up firsttime with user's triplestore + // if the triples which should be added are not already in user´s triplestore, add them + if (!userModel.containsAll(difNewOld)) { + log.debug("Some of these triples are not in the user triples store, so they will be added now"); + // but only the triples that are no already there + Model tmp = difNewOld.difference(userModel); + userModel.add(tmp); + updatedFiles = true; + } + + // add the triples from the back up firsttime model for the next check + baseModel.add(difNewOld); + } + } + return updatedFiles; + } + + /** + * Check if the UI-changes Overlap with the changes made in the fristtime-files, if they overlap these changes are not applied to the user-model (UI) + * + * @param baseModel firsttime backup model + * @param userModel current state in the system (user/UI-model) + * @param changesModel the changes between firsttime-files and firttime-backup + */ + private void checkUiChangesOverlapWithFileChanges(Model baseModel, Model userModel, Model changesModel) { + log.debug("Beginn check if subtractions from Backup-firsttime model to current state of firsttime-files were changed in user-model (via UI)"); + Model changesUserModel = userModel.difference(baseModel); + List changedInUIandFileStatements = new ArrayList(); + + if(!changesUserModel.isEmpty()) + { + removeBlankTriples(changesUserModel); + + StringWriter out3 = new StringWriter(); + changesUserModel.write(out3, "TTL"); + log.debug("There were changes in the user-model via UI which have also changed in the firsttime files, the following triples will not be updated"); + + // iterate all statements and check if the ones which should be removed were not changed via the UI + StmtIterator iter = changesUserModel.listStatements(); + while (iter.hasNext()) { + Statement stmt = iter.nextStatement(); // get next statement + Resource subject = stmt.getSubject(); // get the subject + Property predicate = stmt.getPredicate(); // get the predicate + RDFNode object = stmt.getObject(); // get the object + + StmtIterator iter2 = changesModel.listStatements(); + + while (iter2.hasNext()) { + Statement stmt2 = iter2.nextStatement(); // get next statement + Resource subject2 = stmt2.getSubject(); // get the subject + Property predicate2 = stmt2.getPredicate(); // get the predicate + RDFNode object2 = stmt2.getObject(); // get the object + + // if subject and predicate are equal but the object differs and the language tag is the same, do not update these triples + // this case indicates an change in the UI, which should not be overwriten from the firsttime files + if(subject.equals(subject2) && predicate.equals(predicate2) && !object.equals(object2) ) { + // if object is an literal, check the language tag + if (object.isLiteral() && object2.isLiteral()) { + // if the langauge tag is the same, remove this triple from the update list + if(object.asLiteral().getLanguage().equals(object2.asLiteral().getLanguage())) { + log.debug("This two triples changed UI and files: \n UI: " + stmt + " \n file: " +stmt2); + changedInUIandFileStatements.add(stmt2); + } + } else { + log.debug("This two triples changed UI and files: \n UI: " + stmt + " \n file: " +stmt2); + changedInUIandFileStatements.add(stmt2); + } + } + } + } + // remove triples which were changed in the user model (UI) from the list + changesModel.remove(changedInUIandFileStatements); + } else { + log.debug("There were no changes in the user-model via UI compared to the backup-firsttime-model"); + } + } + + /** + * Remove all triples where subject or object is blank (Anon) + */ + private void removeBlankTriples(Model model) { + StmtIterator iter = model.listStatements(); + List removeStatement = new ArrayList(); + while (iter.hasNext()) { + Statement stmt = iter.nextStatement(); // get next statement + Resource subject = stmt.getSubject(); // get the subject + RDFNode object = stmt.getObject(); // get the object + + if(subject.isAnon() || object.isAnon()) + { + removeStatement.add(stmt); + } + } + model.remove(removeStatement); } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ContentModelSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ContentModelSetup.java index 84e2d6809..2024e59e8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ContentModelSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ContentModelSetup.java @@ -5,9 +5,13 @@ package edu.cornell.mannlib.vitro.webapp.servlet.setup; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.ABOX_ASSERTIONS; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.APPLICATION_METADATA; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_ASSERTIONS; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.ABOX_ASSERTIONS_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_ASSERTIONS_FIRSTTIME_BACKUP; +import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.APPLICATION_METADATA_FIRSTTIME_BACKUP; import java.util.ArrayList; import java.util.List; +import java.io.StringWriter; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; @@ -17,12 +21,16 @@ import org.apache.commons.logging.LogFactory; import org.apache.jena.ontology.OntModel; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.ResIterator; import org.apache.jena.rdf.model.Resource; import org.apache.jena.shared.Lock; import org.apache.jena.util.ResourceUtils; import org.apache.jena.util.iterator.ClosableIterator; import org.apache.jena.vocabulary.RDF; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.rdf.model.RDFNode; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; @@ -60,22 +68,39 @@ public class ContentModelSetup extends JenaDataSourceSetupBase Model applicationMetadataModel = models.getOntModel(APPLICATION_METADATA); if (applicationMetadataModel.isEmpty()) { firstTimeStartup = true; - initializeApplicationMetadata(ctx, applicationMetadataModel); + initializeApplicationMetadata(ctx, applicationMetadataModel); + + // backup copy from firsttime files + OntModel applicationMetadataModelFirsttime = models.getOntModel(APPLICATION_METADATA_FIRSTTIME_BACKUP); + applicationMetadataModelFirsttime.add(applicationMetadataModel); + } else { + // check if some of the firsttime files have changed since the first start up and + // if they changed, apply these changes to the user models + applyFirstTimeChanges(ctx); + checkForNamespaceMismatch( applicationMetadataModel, ctx ); } OntModel baseABoxModel = models.getOntModel(ABOX_ASSERTIONS); if (firstTimeStartup) { - RDFFilesLoader.loadFirstTimeFiles("abox", baseABoxModel, true); + RDFFilesLoader.loadFirstTimeFiles(ctx, "abox", baseABoxModel, true); + + // backup copy from firsttime files + OntModel baseABoxModelFirsttime = models.getOntModel(ABOX_ASSERTIONS_FIRSTTIME_BACKUP); + baseABoxModelFirsttime.add(baseABoxModel); } - RDFFilesLoader.loadEveryTimeFiles("abox", baseABoxModel); + RDFFilesLoader.loadEveryTimeFiles(ctx, "abox", baseABoxModel); OntModel baseTBoxModel = models.getOntModel(TBOX_ASSERTIONS); if (firstTimeStartup) { - RDFFilesLoader.loadFirstTimeFiles("tbox", baseTBoxModel, true); + RDFFilesLoader.loadFirstTimeFiles(ctx, "tbox", baseTBoxModel, true); + + // backup copy from firsttime files + OntModel baseTBoxModelFirsttime = models.getOntModel(TBOX_ASSERTIONS_FIRSTTIME_BACKUP); + baseTBoxModelFirsttime.add(baseTBoxModel); } - RDFFilesLoader.loadEveryTimeFiles("tbox", baseTBoxModel); + RDFFilesLoader.loadEveryTimeFiles(ctx, "tbox", baseTBoxModel); } private long secondsSince(long startTime) { @@ -94,7 +119,7 @@ public class ContentModelSetup extends JenaDataSourceSetupBase private void initializeApplicationMetadata(ServletContext ctx, Model applicationMetadataModel) { OntModel temporaryAMModel = VitroModelFactory.createOntologyModel(); - RDFFilesLoader.loadFirstTimeFiles("applicationMetadata", temporaryAMModel, true); + RDFFilesLoader.loadFirstTimeFiles(ctx, "applicationMetadata", temporaryAMModel, true); setPortalUriOnFirstTime(temporaryAMModel, ctx); applicationMetadataModel.add(temporaryAMModel); } @@ -191,6 +216,188 @@ public class ContentModelSetup extends JenaDataSourceSetupBase } } + /* + * Check if the firsttime files have changed since the firsttime startup for all ContentModels, + * if so, then apply the changes but not overwrite the whole user model + */ + private void applyFirstTimeChanges(ServletContext ctx) { + + applyFirstTimeChanges(ctx, "applicationMetadata", APPLICATION_METADATA_FIRSTTIME_BACKUP, APPLICATION_METADATA); + + applyFirstTimeChanges(ctx, "abox", ABOX_ASSERTIONS_FIRSTTIME_BACKUP, ABOX_ASSERTIONS); + + applyFirstTimeChanges(ctx, "tbox", TBOX_ASSERTIONS_FIRSTTIME_BACKUP, TBOX_ASSERTIONS); + } + + + /* + * Check if the firsttime files have changed since the firsttime startup for one ContentModel, + * if so, then apply the changes but not overwrite the whole user model + */ + private void applyFirstTimeChanges(ServletContext ctx, String modelPath, String firsttimeBackupModelUri, String userModelUri) { + log.info("Reload firsttime files on start-up if changed: '" + modelPath +"', URI: '" +userModelUri+ "'"); + ContextModelAccess models = ModelAccess.on(ctx); + OntModel firsttimeBackupModel = models.getOntModel(firsttimeBackupModelUri); + + // compare firsttime files with configuration models + log.debug("compare firsttime files with configuration models (backup from first start) for " + modelPath); + OntModel firsttimeFilesModel = VitroModelFactory.createOntologyModel(); + RDFFilesLoader.loadFirstTimeFiles(ctx, modelPath, firsttimeFilesModel, true); + + // special initialization for application metadata model + if (firsttimeBackupModelUri.equals(APPLICATION_METADATA_FIRSTTIME_BACKUP)) { + setPortalUriOnFirstTime(firsttimeFilesModel, ctx); + } + + if ( firsttimeBackupModel.isIsomorphicWith(firsttimeFilesModel) ) { + log.debug("They are the same, so do nothing: '" + modelPath + "'"); + } else { + log.debug("They differ: '" + modelPath + "', compare values in configuration models with user's triplestore"); + OntModel userModel = models.getOntModel(userModelUri); + + // double check the statements (blank notes, etc.) and apply the changes + boolean updatedFiles = applyChanges(firsttimeBackupModel, firsttimeFilesModel, userModel, modelPath); + if (updatedFiles) log.info("The model was updated, " + modelPath); + } + } + + /* + * This method is designed to compare configuration models (baseModel) with firsttime files (newModel): + * if they are the same, stopFirstTime + * else, if they differ, compare values in configuration models (baseModel) with user's triplestore + * if they are the same, update user's triplestore with value in new firsttime files + * else, if they differ, leave user's triplestore statement alone + * finally, overwrite the configuration models with content of the updated firstime files + * + * @param baseModel The backup firsttime model (from the first startup) + * @param newModel The current state of the firsttime files in the directory + * @param userModel The current state of the user model + * @param modelIdString Just an string for the output for better debugging (tbox, abox, applicationMetadata) + */ + private boolean applyChanges(Model baseModel, Model newModel, Model userModel, String modelIdString) { + boolean updatedFiles = false; + StringWriter out = new StringWriter(); + StringWriter out2 = new StringWriter(); + Model difOldNew = baseModel.difference(newModel); + Model difNewOld = newModel.difference(baseModel); + + // special case for "rootTab" triple, do not need an update (is it still used in general? if not remove this case) + if(modelIdString.equals("applicationMetadata")) { + + Property p = userModel.createProperty("http://vitro.mannlib.cornell.edu/ns/vitro/0.7#", "rootTab"); + difOldNew.removeAll(null, p, null); + difNewOld.removeAll(null, p, null); + } + + if (difOldNew.isEmpty() && difNewOld.isEmpty()) { + // if there is no difference, nothing needs to be done + log.debug("For the " + modelIdString + " model, there is no difference in both directions. So do nothing."); + } else { + // if there is a difference, we need to remove the triples in difOldNew and + // add the triples in difNewOld to the back up firsttime model + + if (!difOldNew.isEmpty()) { + difOldNew.write(out, "TTL"); + log.debug("Difference for " + modelIdString + " (old -> new), these triples should be removed: " + out); + + // Check if the UI-changes Overlap with the changes made in the fristtime-files + checkUiChangesOverlapWithFileChanges(baseModel, userModel, difOldNew); + + // before we remove the triples, we need to compare values in back up firsttime with user's triplestore + // if the triples which should be removed are still in user´s triplestore, remove them + if (userModel.containsAny(difOldNew)) { + log.debug("Some of these triples are in the user triples store, so they will be removed now"); + userModel.remove(difOldNew); + updatedFiles = true; + } + + // remove the triples from the back up firsttime model for the next check + baseModel.remove(difOldNew); + + } + if (!difNewOld.isEmpty()) { + difNewOld.write(out2, "TTL"); + log.debug("Difference for " + modelIdString + " (new -> old), these triples should be added: " + out2); + + // Check if the UI-changes Overlap with the changes made in the fristtime-files + checkUiChangesOverlapWithFileChanges(baseModel, userModel, difNewOld); + + // before we add the triples, we need to compare values in back up firsttime with user's triplestore + // if the triples which should be added are not already in user´s triplestore, add them + if (!userModel.containsAll(difNewOld)) { + log.debug("Some of these triples are not in the user triples store, so they will be added now"); + // but only the triples that are no already there + Model tmp = difNewOld.difference(userModel); + userModel.add(tmp); + updatedFiles = true; + } + + // add the triples from the back up firsttime model for the next check + baseModel.add(difNewOld); + } + } + return updatedFiles; + } + + /** + * Check if the UI-changes Overlap with the changes made in the fristtime-files, if they overlap these changes are not applied to the user-model (UI) + * + * @param baseModel firsttime backup model + * @param userModel current state in the system (user/UI-model) + * @param changesModel the changes between firsttime-files and firttime-backup + */ + private void checkUiChangesOverlapWithFileChanges(Model baseModel, Model userModel, Model changesModel) { + log.debug("Beginn check if subtractions from Backup-firsttime model to current state of firsttime-files were changed in user-model (via UI)"); + Model changesUserModel = userModel.difference(baseModel); + List changedInUIandFileStatements = new ArrayList(); + + if(!changesUserModel.isEmpty()) + { + + StringWriter out3 = new StringWriter(); + changesUserModel.write(out3, "TTL"); + log.debug("There were changes in the user-model via UI which have also changed in the firsttime files, the following triples will not be updated"); + + // iterate all statements and check if the ones which should be removed were not changed via the UI + StmtIterator iter = changesUserModel.listStatements(); + while (iter.hasNext()) { + Statement stmt = iter.nextStatement(); // get next statement + Resource subject = stmt.getSubject(); // get the subject + Property predicate = stmt.getPredicate(); // get the predicate + RDFNode object = stmt.getObject(); // get the object + + StmtIterator iter2 = changesModel.listStatements(); + + while (iter2.hasNext()) { + Statement stmt2 = iter2.nextStatement(); // get next statement + Resource subject2 = stmt2.getSubject(); // get the subject + Property predicate2 = stmt2.getPredicate(); // get the predicate + RDFNode object2 = stmt2.getObject(); // get the object + + // if subject and predicate are equal but the object differs and the language tag is the same, do not update these triples + // this case indicates an change in the UI, which should not be overwriten from the firsttime files + if(subject.equals(subject2) && predicate.equals(predicate2) && !object.equals(object2) ) { + // if object is an literal, check the language tag + if (object.isLiteral() && object2.isLiteral()) { + // if the langauge tag is the same, remove this triple from the update list + if(object.asLiteral().getLanguage().equals(object2.asLiteral().getLanguage())) { + log.debug("This two triples changed UI and files: \n UI: " + stmt + " \n file: " +stmt2); + changedInUIandFileStatements.add(stmt2); + } + } else { + log.debug("This two triples changed UI and files: \n UI: " + stmt + " \n file: " +stmt2); + changedInUIandFileStatements.add(stmt2); + } + } + } + } + // remove triples which were changed in the user model (UI) from the list + changesModel.remove(changedInUIandFileStatements); + } else { + log.debug("There were no changes in the user-model via UI compared to the backup-firsttime-model"); + } + } + /* ===================================================================== */ @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/FileGraphSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/FileGraphSetup.java index e49f0e3df..80656403b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/FileGraphSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/FileGraphSetup.java @@ -3,6 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.servlet.setup; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT; +import static edu.cornell.mannlib.vitro.webapp.servlet.setup.RDFFilesLoader.getEnabledLocales; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -46,6 +47,7 @@ public class FileGraphSetup implements ServletContextListener { private static final Log log = LogFactory.getLog(FileGraphSetup.class); private static final String RDF = "rdf"; + private static final String I18N = "i18n"; private static final String ABOX = "abox"; private static final String TBOX = "tbox"; private static final String FILEGRAPH = "filegraph"; @@ -76,6 +78,12 @@ public class FileGraphSetup implements ServletContextListener { // ABox files Set paths = getFilegraphPaths(ctx, RDF, ABOX, FILEGRAPH); + // Load ABox files from enabled languages + Set enabledLocales = getEnabledLocales(ctx); + for (String locale : enabledLocales) { + paths.addAll(getFilegraphPaths(ctx, RDF, I18N, locale, ABOX, FILEGRAPH)); + } + cleanupDB(dataset, pathsToURIs(paths, ABOX), ABOX); // Just update the ABox filegraphs in the DB; don't attach them to a base model. @@ -84,6 +92,11 @@ public class FileGraphSetup implements ServletContextListener { // TBox files paths = getFilegraphPaths(ctx, RDF, TBOX, FILEGRAPH); + // Load TBox files from enabled languages + for (String locale : enabledLocales) { + paths.addAll(getFilegraphPaths(ctx, RDF, I18N, locale, TBOX, FILEGRAPH)); + } + cleanupDB(dataset, pathsToURIs(paths, TBOX),TBOX); OntModel tboxBaseModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_ASSERTIONS); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/RDFFilesLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/RDFFilesLoader.java index 35fdaf54d..acaa93fb6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/RDFFilesLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/RDFFilesLoader.java @@ -2,7 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.servlet.setup; -import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -10,9 +9,13 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.TreeSet; +import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -23,6 +26,8 @@ import org.apache.jena.rdf.model.ModelFactory; import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; +import javax.servlet.ServletContext; + /** * Help to load RDF files on first time and on every startup. */ @@ -31,11 +36,12 @@ public class RDFFilesLoader { private static final String DEFAULT_RDF_FORMAT = "RDF/XML"; private static final String RDF = "rdf"; + private static final String I18N = "i18n"; private static final String FIRST_TIME = "firsttime"; private static final String EVERY_TIME = "everytime"; /** - * Path filter that ignores sub-directories, hidden files, and markdown + * Path filter that ignores sub-directories, hidden files and markdown * files. */ private static final DirectoryStream.Filter RDF_FILE_FILTER = new DirectoryStream.Filter() { @@ -64,11 +70,20 @@ public class RDFFilesLoader { * * The files from the directory are added to the model. */ - public static void loadFirstTimeFiles(String modelPath, Model model, + public static void loadFirstTimeFiles(ServletContext ctx, String modelPath, Model model, boolean firstTime) { if (firstTime) { String home = locateHomeDirectory(); + + // Load common files Set paths = getPaths(home, RDF, modelPath, FIRST_TIME); + + // Load enabled languages + Set enabledLocales = getEnabledLocales(ctx); + for (String locale : enabledLocales) { + paths.addAll(getPaths(home, RDF, I18N, locale, modelPath, FIRST_TIME)); + } + for (Path p : paths) { log.info("Loading " + relativePath(p, home)); readOntologyFileIntoModel(p, model); @@ -87,11 +102,20 @@ public class RDFFilesLoader { * * The files from the directory become a sub-model of the model. */ - public static void loadEveryTimeFiles(String modelPath, OntModel model) { + public static void loadEveryTimeFiles(ServletContext ctx, String modelPath, OntModel model) { OntModel everytimeModel = ModelFactory .createOntologyModel(OntModelSpec.OWL_MEM); String home = locateHomeDirectory(); + + // Load common files Set paths = getPaths(home, RDF, modelPath, EVERY_TIME); + + // Load enabled languages + Set enabledLocales = getEnabledLocales(ctx); + for (String locale : enabledLocales) { + paths.addAll(getPaths(home, RDF, I18N, locale, modelPath, EVERY_TIME)); + } + for (Path p : paths) { log.info("Loading " + relativePath(p, home)); readOntologyFileIntoModel(p, everytimeModel); @@ -99,6 +123,23 @@ public class RDFFilesLoader { model.addSubModel(everytimeModel); } + public static Set getEnabledLocales(ServletContext ctx) { + Set enabledLocales = new HashSet<>(); + + // Which locales are enabled in runtime.properties? + List locales = SelectedLocale.getSelectableLocales(ctx); + for (Locale locale : locales) { + enabledLocales.add(locale.toLanguageTag().replace('-', '_')); + } + + // If no languages were enabled in runtime.properties, add a fallback as the default + if (enabledLocales.isEmpty()) { + enabledLocales.add(SelectedLocale.getFallbackLocale().toString()); + } + + return enabledLocales; + } + private static Path relativePath(Path p, String home) { try { return Paths.get(home).relativize(p); @@ -107,36 +148,9 @@ public class RDFFilesLoader { } } - /** - * Create a model from all the RDF files in the specified directory. - */ - public static OntModel getModelFromDir(File dir) { - OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); - if (dir == null) { - log.warn("Must pass a File to getModelFromDir()"); - return model; - } - if (!dir.isDirectory()) { - log.warn("Directory must be a File object for a directory"); - return model; - } - if (!dir.canRead()) { - log.warn("getModelFromDir(): Directory " - + " must be readable, check permissions on " - + dir.getAbsolutePath()); - return model; - } - - Set paths = getPaths(dir.getPath()); - for (Path p : paths) { - readOntologyFileIntoModel(p, model); - } - return model; - } - /** * Find the paths to RDF files in this directory. Sub-directories, hidden - * files, and markdown files are ignored. + * files, markdown, and non-enabled language files are ignored. */ private static Set getPaths(String parentDir, String... strings) { Path dir = Paths.get(parentDir, strings); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java index 9cc824748..1adb6c694 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java @@ -6,7 +6,6 @@ import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.APPLICATIO import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.servlet.ServletContext; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java index 00fdcf1b0..1bce3fbad 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java @@ -534,8 +534,12 @@ public class UpdateKnowledgeBase { try { if (f.getName().endsWith(".md")) { // Markdown files are documentation - skip. - } else if (f.getName().endsWith(".n3")) { + // UQAM-Optimization accept lower and upper case in fn extension + } else if (f.getName().toLowerCase().endsWith(".n3")) { om.read(fis, null, "N3"); + // UQAM-Optimization Accept Turtle (Must for us) + } else if (f.getName().toLowerCase().endsWith(".ttl")) { + om.read(fis, null, "TURTLE"); } else { om.read(fis, null, "RDF/XML"); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/BasicShortTermCombinedTripleSource.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/BasicShortTermCombinedTripleSource.java index 5d43d04b7..93bc5f895 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/BasicShortTermCombinedTripleSource.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/BasicShortTermCombinedTripleSource.java @@ -98,9 +98,11 @@ public class BasicShortTermCombinedTripleSource implements @Override public WebappDaoFactoryConfig getWebappDaoFactoryConfig() { List langs = getPreferredLanguages(); + List locales = Collections.list(getPreferredLocales()); WebappDaoFactoryConfig config = new WebappDaoFactoryConfig(); config.setDefaultNamespace(props.getProperty("Vitro.defaultNamespace")); config.setPreferredLanguages(langs); + config.setPreferredLocales(locales); config.setUnderlyingStoreReasoned(isStoreReasoned()); config.setCustomListViewConfigFileMap(getCustomListViewConfigFileMap()); return config; @@ -111,7 +113,6 @@ public class BasicShortTermCombinedTripleSource implements return LanguageFilteringUtils.localesToLanguages(getPreferredLocales()); } - @SuppressWarnings("unchecked") private Enumeration getPreferredLocales() { return req.getLocales(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java index 7c1da263e..e5c057778 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java @@ -165,7 +165,16 @@ public class SparqlQueryDataGetter extends DataGetterBase implements DataGetter{ private String bindParameters(String text, Map merged) { String bound = text; for (String key : merged.keySet()) { - bound = bound.replaceAll("([?$]" + key + ")([^a-zA-Z0-9_\\-])", "<" + merged.get(key) + ">$2"); + String value = merged.get(key); + if (value.startsWith("http://") || value.startsWith("https://")) { + /* + * UQAM-Optimization if the "value" looks like an URI then wrap the value with the characters '<' '>' + * + */ + bound = bound.replaceAll("([?$]" + key + ")([^a-zA-Z0-9_\\-])", "<" + value + ">$2"); + } else { + bound = bound.replaceAll("([?$]" + key + ")([^a-zA-Z0-9_\\-])", value + "$2"); + } } if (log.isDebugEnabled()) { log.debug("parameters: " + merged); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java index 4414c1b8d..1037330e5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java @@ -43,7 +43,10 @@ public enum Key { * Load language property files every time they are requested. */ I18N_DEFEAT_CACHE("developer.i18n.defeatCache", true), - + /** + * Enable online translations. + */ + I18N_ONLINE_TRANSLATION("developer.i18n.onlineTranslation", true), /** * Enable the I18nLogger to log each string request. */ diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/threads/VitroBackgroundThread.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/threads/VitroBackgroundThread.java index d0ff197f7..2d3844933 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/threads/VitroBackgroundThread.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/threads/VitroBackgroundThread.java @@ -21,6 +21,10 @@ import org.apache.commons.logging.LogFactory; * check their current status. */ public class VitroBackgroundThread extends Thread { + // UQAM-Bug-Correction add start + public synchronized void start() { + super.start(); + } private static final Log log = LogFactory.getLog(VitroBackgroundThread.class); private static final ConcurrentLinkedQueue> allThreads = new ConcurrentLinkedQueue>(); 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 081690415..89dc8c1bb 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 @@ -20,8 +20,6 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; -import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java index 7f88848ec..c3bd701dd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java @@ -6,7 +6,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.text.Collator; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -39,6 +38,8 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.SelectListGeneratorVTwo; import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LanguageOption; import edu.cornell.mannlib.vitro.webapp.web.beanswrappers.ReadOnlyBeansWrapper; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.ObjectPropertyStatementTemplateModel; @@ -98,7 +99,9 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { private void populateDropdowns() throws Exception { //For each field with an optionType defined, create the options - WebappDaoFactory wdf = vreq.getWebappDaoFactory(); +// WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + // UQAM-Optimization Manage Linguistic context + WebappDaoFactory wdf = ModelAccess.on(vreq).getWebappDaoFactory(LanguageOption.LANGUAGE_AWARE); for(String fieldName: editConfig.getFields().keySet()){ FieldVTwo field = editConfig.getField(fieldName); //TODO: Check if we even need empty options if field options do not exist @@ -106,7 +109,8 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { //empty options field.setOptions(new ConstantFieldOptions()); } - Map optionsMap = SelectListGeneratorVTwo.getOptions(editConfig, fieldName, wdf); + //UQAM-Optimization changing signature for including internationalization in scroll-down menu + Map optionsMap = SelectListGeneratorVTwo.getOptions(editConfig, fieldName, wdf, I18n.bundle(vreq)); optionsMap = SelectListGeneratorVTwo.getSortedMap(optionsMap, field.getFieldOptions().getCustomComparator(), vreq); if(pageData.containsKey(fieldName)) { log.error("Check the edit configuration setup as pageData already contains " + fieldName + " and this will be overwritten now with empty collection"); @@ -129,6 +133,11 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { return selectedValue; } + public String getPageTitle() { + String pageTitle = i18n.text("edit_page_title"); + return pageTitle != null ? pageTitle : "Edit"; + } + private void setFormTitle() { String predicateUri = editConfig.getPredicateUri(); if(predicateUri != null) { @@ -184,7 +193,7 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { Individual objectIndividual = EditConfigurationUtils.getObjectIndividual(vreq); ObjectProperty prop = EditConfigurationUtils.getObjectProperty(vreq); Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq); - VClass rangeClass = EditConfigurationUtils.getRangeVClass(vreq); + VClass rangeClass = EditConfigurationUtils.getLangAwardRangeVClass(vreq); if(objectIndividual != null) { propertyTitle = prop.getDomainPublic(); } else { @@ -529,14 +538,17 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { //TODO:Check where this logic should actually go, copied from input element formatting tag //Updating to enable multiple vclasses applicable to subject to be analyzed to understand possible range of types public Map getOfferTypesCreateNew() { - WebappDaoFactory wdf = vreq.getWebappDaoFactory(); +// WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + // UQAM-Optimization Manage Linguistic context + WebappDaoFactory wdf = ModelAccess.on(vreq).getWebappDaoFactory(LanguageOption.LANGUAGE_AWARE); ObjectProperty op = wdf.getObjectPropertyDao().getObjectPropertyByURI(editConfig.getPredicateUri()); Individual sub = wdf.getIndividualDao().getIndividualByURI(editConfig.getSubjectUri()); - VClass rangeClass = EditConfigurationUtils.getRangeVClass(vreq); + // UQAM-Optimization Manage Linguistic context + VClass rangeClass = EditConfigurationUtils.getLangAwardRangeVClass(vreq); List vclasses = null; List subjectVClasses = sub.getVClasses(); diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java b/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java index 6e99778d0..e4427d82e 100644 --- a/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java @@ -1,15 +1,24 @@ package org.vivoweb.linkeddatafragments.servlet; -import com.fasterxml.jackson.databind.JsonNode; -import edu.cornell.mannlib.vitro.webapp.beans.Ontology; -import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; -import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; -import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; -import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.apache.commons.io.IOUtils; -import org.apache.jena.riot.Lang; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.riot.Lang; import org.linkeddatafragments.config.ConfigReader; import org.linkeddatafragments.datasource.DataSourceFactory; import org.linkeddatafragments.datasource.DataSourceTypesRegistry; @@ -22,26 +31,19 @@ import org.linkeddatafragments.fragments.ILinkedDataFragment; import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; import org.linkeddatafragments.util.MIMEParse; import org.linkeddatafragments.views.ILinkedDataFragmentWriter; -import org.vivoweb.linkeddatafragments.views.HtmlTriplePatternFragmentWriterImpl; -import org.vivoweb.linkeddatafragments.views.LinkedDataFragmentWriterFactory; import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceBasedRequestProcessorForTPFs; import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceDataSourceType; +import org.vivoweb.linkeddatafragments.views.HtmlTriplePatternFragmentWriterImpl; +import org.vivoweb.linkeddatafragments.views.LinkedDataFragmentWriterFactory; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map.Entry; +import com.fasterxml.jackson.databind.JsonNode; + +import edu.cornell.mannlib.vitro.webapp.beans.Ontology; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; +import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; /** * Servlet that responds with a Linked Data Fragment. @@ -52,29 +54,13 @@ public class VitroLinkedDataFragmentServlet extends VitroHttpServlet { private final static long serialVersionUID = 1L; private static final String PROPERTY_TPF_ACTIVE_FLAG = "tpf.activeFlag"; + private static final Log log = LogFactory.getLog(VitroLinkedDataFragmentServlet.class); private ConfigReader config; private final HashMap dataSources = new HashMap<>(); - private final Collection mimeTypes = new ArrayList<>(); private ConfigurationProperties configProps; private String tpfActiveFlag; - private File getConfigFile(ServletConfig config) throws IOException { - String path = config.getServletContext().getRealPath("/"); - if (path == null) { - // this can happen when running standalone - path = System.getProperty("user.dir"); - } - File cfg = new File(path, "config-example.json"); - if (!cfg.exists()) { - throw new IOException("Configuration file " + cfg + " not found."); - } - if (!cfg.isFile()) { - throw new IOException("Configuration file " + cfg + " is not a file."); - } - return cfg; - } - @Override public void init(ServletConfig servletConfig) throws ServletException { try { @@ -82,11 +68,11 @@ public class VitroLinkedDataFragmentServlet extends VitroHttpServlet { configProps = ConfigurationProperties.getBean(ctx); if (!configurationPresent()) { - throw new ServletException("TPF is currently disabled. To enable, add 'tpfActive.flag=true' to the runtime.properties."); - } else { - if (!tpfActiveFlag.equalsIgnoreCase("true")) { - throw new ServletException("TPF is currently disabled. To enable, set 'tpfActive.flag=true' in runtime.properties."); - } + throw new ServletException("TPF is currently disabled. To enable, add '" + + PROPERTY_TPF_ACTIVE_FLAG + " = true' to runtime.properties."); + } else if (!tpfActiveFlag.equalsIgnoreCase("true")) { + throw new ServletException("TPF is currently disabled. To enable, set '" + + PROPERTY_TPF_ACTIVE_FLAG + " = true' in runtime.properties."); } RDFService rdfService = ModelAccess.on(ctx).getRDFService(); @@ -215,18 +201,18 @@ public class VitroLinkedDataFragmentServlet extends VitroHttpServlet { writer.writeFragment(response.getOutputStream(), dataSource, fragment, ldfRequest); } catch (DataSourceNotFoundException ex) { + log.error(ex, ex); try { response.setStatus(404); writer.writeNotFound(response.getOutputStream(), request); } catch (Exception ex1) { + log.error(ex1, ex1); throw new ServletException(ex1); } - } catch (Exception e) { - response.setStatus(500); - writer.writeError(response.getOutputStream(), e); - } + } } catch (Exception e) { + log.error(e, e); throw new ServletException(e); } finally { diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/testing/AbstractTestClass.java b/api/src/test/java/edu/cornell/mannlib/vitro/testing/AbstractTestClass.java index fa5e4fe01..77d669415 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/testing/AbstractTestClass.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/testing/AbstractTestClass.java @@ -56,6 +56,7 @@ import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import stubs.edu.cornell.mannlib.vitro.webapp.i18n.I18nStub; /** * A collection of useful routines to help when testing. @@ -85,8 +86,21 @@ public abstract class AbstractTestClass { * {@link Level#INFO}. */ @Before + public void setUp() { + initializeLogging(); + useI18nStubBundles(); + } + @After - public void initializeLogging() { + public void tearDown() { + initializeLogging(); + } + + protected void useI18nStubBundles() { + I18nStub.setup(); + } + + private void initializeLogging() { LogManager.resetConfiguration(); Logger.getRootLogger().addAppender(new ConsoleAppender(patternLayout)); Logger.getRootLogger().setLevel(Level.INFO); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicyTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicyTest.java index 68b0ab4cd..17ef2333a 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicyTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicyTest.java @@ -67,7 +67,7 @@ public class SelfEditingPolicyTest extends AbstractTestClass { private OntModel ontModel; @Before - public void setUp() throws Exception { + public void setUp() { ctx = new ServletContextStub(); PropertyRestrictionBeanStub diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicy_2_Test.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicy_2_Test.java index 044318139..ce7749868 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicy_2_Test.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/auth/policy/SelfEditingPolicy_2_Test.java @@ -75,7 +75,7 @@ public class SelfEditingPolicy_2_Test extends AbstractTestClass { @Before - public void setUp() throws Exception { + public void setUp() { ServletContextStub ctx = new ServletContextStub(); PropertyRestrictionBeanStub.getInstance(new String[] { ADMIN_NS }); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiTest.java index 8fde7a742..aeed2e551 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiTest.java @@ -73,7 +73,6 @@ public class SparqlUpdateApiTest extends AbstractTestClass { try { if(ds.supportsTransactions()) { ds.begin(ReadWrite.WRITE); - System.out.println("yep"); } UpdateAction.execute(UpdateFactory.create(updateStr1), graphStore); } finally { 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 f15e05567..c849fa411 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 @@ -48,7 +48,6 @@ import edu.cornell.mannlib.vitro.webapp.auth.policy.ServletPolicyList; import edu.cornell.mannlib.vitro.webapp.beans.PermissionSet; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; -import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.AuthenticatorStub; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.State; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java index 8548ea76d..6e436c8f8 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java @@ -11,6 +11,7 @@ import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.OWL; import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; @@ -65,9 +66,9 @@ public class DataPropertyDaoJenaTest extends AbstractTestClass { property1.setPropertyValue(RDFS.domain, subModel.createResource("http://thisIsTheDomainClassURI")); property1.setPropertyValue(RDFS.range, subModel.createResource("http://thisIsTheRangeClassURI")); property1.addProperty(RDF.type, OWL.FunctionalProperty); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), subModel.createTypedLiteral("this is the example")); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the description")); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PUBLIC_DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the public description")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), ResourceFactory.createLangLiteral("this is the example", lang)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), ResourceFactory.createLangLiteral("this is the description", lang)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PUBLIC_DESCRIPTION_ANNOT), ResourceFactory.createLangLiteral("this is the public description", lang)); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_RANK_ANNOT), subModel.createTypedLiteral(21)); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_LIMIT), subModel.createTypedLiteral(5)); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT), subModel.createResource("http://vitro.mannlib.cornell.edu/ns/vitro/role#curator")); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DependentResourceDeleteJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DependentResourceDeleteJenaTest.java index 2428b56de..24b98809d 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DependentResourceDeleteJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DependentResourceDeleteJenaTest.java @@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.io.StringReader; import java.util.List; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Assert; import org.apache.jena.rdf.model.Model; @@ -16,7 +17,7 @@ import org.apache.jena.vocabulary.XSD; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -public class DependentResourceDeleteJenaTest { +public class DependentResourceDeleteJenaTest extends AbstractTestClass { String isDependentRelation = " <"+VitroVocabulary.PROPERTY_STUBOBJECTPROPERTYANNOT+"> \"true\"^^xsd:boolean .\n" ; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoTest.java index 01058f799..fe3938974 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDaoTest.java @@ -2,26 +2,29 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import static org.junit.Assert.assertEquals; + import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.StringReader; -import org.junit.Assert; - -import org.junit.Test; - import org.apache.jena.ontology.OntClass; import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.ontology.OntProperty; +import org.apache.jena.ontology.OntResource; import org.apache.jena.ontology.Restriction; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.OWL; import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; import org.apache.jena.vocabulary.XSD; +import org.junit.Assert; +import org.junit.Test; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; @@ -35,7 +38,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -public class JenaBaseDaoTest { +public class JenaBaseDaoTest extends AbstractTestClass { String isDependentRelation = " <"+VitroVocabulary.PROPERTY_STUBOBJECTPROPERTYANNOT+"> \"true\"^^xsd:boolean .\n" ; @@ -457,6 +460,53 @@ public class JenaBaseDaoTest { Assert.assertEquals(m.size(), 2); // just rdf:type for Class1 and Prop } + + @Test + /** + * Test that a resource's labels in one language are correctly updated without + * affecting labels in other languages. + */ + public void testUpdateRDFSLabel() { + OntModel m = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + WebappDaoFactoryJena wadf = new WebappDaoFactoryJena(m); + JenaBaseDao dao = new JenaBaseDao(wadf); + OntResource ontRes = m.createOntResource("http://example.com/i/n1"); + // update one language + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US")); + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES")); + dao.updateRDFSLabel(ontRes, "engLabel2", "en-US"); + assertEquals(2, m.size()); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel2", "en-US"))); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES"))); + m.removeAll(); + // update language-less + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US")); + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES")); + m.add(ontRes, RDFS.label, "languageLessLabel1"); + dao.updateRDFSLabel(ontRes, "languageLessLabel2", null); + assertEquals(3, m.size()); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US"))); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES"))); + Assert.assertTrue(m.contains(ontRes, RDFS.label, "languageLessLabel2")); + m.removeAll(); + // remove a language + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US")); + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES")); + m.add(ontRes, RDFS.label, m.createTypedLiteral("stringLabel1")); + dao.updateRDFSLabel(ontRes, null, "en-US"); + assertEquals(2, m.size()); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES"))); + Assert.assertTrue(m.contains(ontRes, RDFS.label, m.createTypedLiteral("stringLabel1"))); + m.removeAll(); + // remove language-less labels + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US")); + m.add(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES")); + m.add(ontRes, RDFS.label, ResourceFactory.createPlainLiteral("languageLessLabel1")); + dao.updateRDFSLabel(ontRes, null, null); + assertEquals(2, m.size()); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("engLabel1", "en-US"))); + Assert.assertTrue(m.contains(ontRes, RDFS.label, ResourceFactory.createLangLiteral("esLabel1", "es-ES"))); + } /** * Compare the contents of the expected model with the actual model (not counting modification times). diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao_2_Test.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao_2_Test.java index 9e448c651..5bbce84bf 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao_2_Test.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao_2_Test.java @@ -48,6 +48,8 @@ public class JenaBaseDao_2_Test extends AbstractTestClass { @Before public void initializeThings() { + super.setUp(); + ontModel = ModelFactory.createOntologyModel(); prop1 = ontModel.createProperty("property1"); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJenaTest.java index 59a8d8bc5..f7d5ca528 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/MenuDaoJenaTest.java @@ -27,7 +27,9 @@ public class MenuDaoJenaTest extends AbstractTestClass { OntModel displayModel; @Before - public void setUp() throws Exception { + public void setUp() { + super.setUp(); + // Suppress error logging. setLoggerLevel(RDFDefaultErrorHandler.class, Level.OFF); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJenaTest.java index 5df236060..32aab2131 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJenaTest.java @@ -9,6 +9,7 @@ import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.RDFS; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; @@ -136,9 +137,9 @@ public class ObjectPropertyDaoJenaTest extends AbstractTestClass { property1.setPropertyValue(RDFS.domain, subModel.createResource("http://thisIsTheDomainClassURI")); property1.setPropertyValue(RDFS.range, subModel.createResource("http://thisIsTheRangeClassURI")); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), subModel.createTypedLiteral("this is the example")); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the description")); - property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PUBLIC_DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the public description")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), ResourceFactory.createLangLiteral("this is the example", lang)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), ResourceFactory.createLangLiteral("this is the description", lang)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PUBLIC_DESCRIPTION_ANNOT), ResourceFactory.createLangLiteral("this is the public description", lang)); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_LIMIT), subModel.createTypedLiteral(6)); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PROPERTY_ENTITYSORTFIELD), subModel.createTypedLiteral("this is the entity sort field")); property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PROPERTY_ENTITYSORTDIRECTION), subModel.createTypedLiteral("this is the entity sort direction")); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJenaTest.java index bc5c8bdef..6488a107b 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJenaTest.java @@ -4,12 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import static org.junit.Assert.*; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Test; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; -public class ObjectPropertyStatementDaoJenaTest { +public class ObjectPropertyStatementDaoJenaTest extends AbstractTestClass { /** * Test if jena lib can parse N3 that it generates. diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/OntModelSegementationTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/OntModelSegementationTest.java index 8867ce9cc..7800acedf 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/OntModelSegementationTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/OntModelSegementationTest.java @@ -2,6 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Assert; import org.junit.Ignore; @@ -28,11 +29,12 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; * @author bjl23 * */ -public class OntModelSegementationTest { +public class OntModelSegementationTest extends AbstractTestClass { private WebappDaoFactoryJena wadf; @org.junit.Before public void setUpWebappDaoFactoryJena() { + super.setUp(); wadf = new WebappDaoFactoryJena(new SimpleOntModelSelector()); } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java index d4b55a540..76e681466 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Assert; import org.apache.jena.ontology.OntModel; @@ -20,7 +21,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -public class PropertyInstanceDaoJenaTest { +public class PropertyInstanceDaoJenaTest extends AbstractTestClass { String isDependentRelation = " <"+VitroVocabulary.PROPERTY_STUBOBJECTPROPERTYANNOT+"> \"true\"^^xsd:boolean .\n" ; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphTest.java new file mode 100644 index 000000000..e5c8a65fa --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphTest.java @@ -0,0 +1,62 @@ +package edu.cornell.mannlib.vitro.webapp.dao.jena; + +import static org.junit.Assert.assertEquals; + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; + + +public class RDFServiceGraphTest extends AbstractTestClass { + + @Test + /** + * Test that creating a new model with the same underlying RDFServiceGraph + * does not result in a new listener registered on that graph. No matter + * how many models have been created using a given RDFServiceGraph, an event + * sent to the last-created model should be heard only once by the + * RDFService. + * @throws RDFServiceException + */ + public void testEventListening() throws RDFServiceException { + Model m = ModelFactory.createDefaultModel(); + RDFService rdfService = new RDFServiceModel(m); + EventsCounter counter = new EventsCounter(); + rdfService.registerListener(counter); + RDFServiceGraph g = new RDFServiceGraph(rdfService); + Model model = null; + for (int i = 0; i < 100; i++) { + model = RDFServiceGraph.createRDFServiceModel(g); + } + model.notifyEvent("event"); + assertEquals(1, counter.getCount()); + } + + private class EventsCounter implements ChangeListener { + + private int count = 0; + + public int getCount() { + return count; + } + + @Override + public void notifyModelChange(ModelChange modelChange) { + // TODO Auto-generated method stub + } + + @Override + public void notifyEvent(String graphURI, Object event) { + count++; + } + + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJenaTest.java index e6a301366..7f755bf42 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJenaTest.java @@ -74,6 +74,8 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass { @Before public void setup() throws IOException { + super.setUp(); + InputStream stream = UserAccountsDaoJenaTest.class .getResourceAsStream(N3_DATA_FILENAME); Model model = ModelFactory.createDefaultModel(); @@ -90,6 +92,8 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass { @Before public void createUserAccountValues() { + super.setUp(); + user1 = userAccount(URI_USER1, "email@able.edu", "Zack", "Roberts", "garbage", "" ,"", 0L, false, 5, 12345678L, Status.ACTIVE, "user1", false, collection(URI_ROLE1), false, EMPTY); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoTest.java index 4bca1fd7e..94068bf97 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoTest.java @@ -3,19 +3,25 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.StringWriter; import java.util.List; -import static org.junit.Assert.*; -import org.junit.Test; - +import org.apache.jena.atlas.logging.Log; import org.apache.jena.ontology.ObjectProperty; import org.apache.jena.ontology.OntClass; import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.OWL; +import org.junit.Test; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; @@ -24,7 +30,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; * */ -public class VClassDaoTest { +public class VClassDaoTest extends AbstractTestClass { @Test // Test that the VClassDaoJena::updateVClass method will only update the jena model for @@ -62,9 +68,9 @@ public class VClassDaoTest { class1.setLabel(rdfsLabel,lang); //rdfs:label class1.setPropertyValue(subModel.createProperty(VitroVocabulary.IN_CLASSGROUP), subModel.createResource("http://thisIsTheClassGroupURI")); - class1.setPropertyValue(subModel.createProperty(VitroVocabulary.SHORTDEF), subModel.createTypedLiteral("this is the short definition")); - class1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), subModel.createTypedLiteral("this is the example - why is this a string?")); - class1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the description")); + class1.setPropertyValue(subModel.createProperty(VitroVocabulary.SHORTDEF), ResourceFactory.createLangLiteral("this is the short definition", lang)); + class1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), ResourceFactory.createLangLiteral("this is the example - why is this a string?", lang)); + class1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), ResourceFactory.createLangLiteral("this is the description", lang)); class1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_LIMIT), subModel.createTypedLiteral(-1)); class1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_RANK_ANNOT), subModel.createTypedLiteral(-11)); class1.setPropertyValue(subModel.createProperty(VitroVocabulary.SEARCH_BOOST_ANNOT), subModel.createTypedLiteral(2.4f)); @@ -120,7 +126,7 @@ public class VClassDaoTest { wipeOutModTime(superModel); assertTrue(subModel.isIsomorphicWith(origSubModel)); - assertTrue(superModel.isIsomorphicWith(origSuperModel)); + assertTrue(superModel.isIsomorphicWith(origSuperModel)); } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCacheTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCacheTest.java index 684686a07..6a60adb2d 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCacheTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCacheTest.java @@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.io.StringReader; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -14,10 +15,12 @@ import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.rdf.model.Statement; import org.apache.jena.vocabulary.RDFS; -public class VClassGroupCacheTest { +public class VClassGroupCacheTest extends AbstractTestClass { @Before - public void setUp() throws Exception { + public void setUp() { + // Not calling super.setUp() because we do not want to change the logging level + super.useI18nStubBundles(); } @Test diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJenaTest.java index e252292d0..3aeb732fc 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJenaTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJenaTest.java @@ -7,6 +7,7 @@ import java.net.URLEncoder; import java.util.Iterator; import java.util.List; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import org.junit.Assert; import org.junit.Test; @@ -49,7 +50,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; * */ -public class VClassJenaTest { +public class VClassJenaTest extends AbstractTestClass { @Test // NIHVIVO-1157 introduced VClassJena.java, a lazy-loading version of VClass.java. diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwoTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwoTest.java index 678ca76d1..4b3b27cfe 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwoTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/BasicValidationVTwoTest.java @@ -4,44 +4,46 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo; import java.util.Collections; -import org.junit.Assert; +import javax.servlet.http.HttpServletRequest; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import stubs.edu.cornell.mannlib.vitro.webapp.i18n.I18nStub; +import stubs.javax.servlet.http.HttpServletRequestStub; + public class BasicValidationVTwoTest { + @Before + public void useI18nStubBundles() { + I18nStub.setup(); + } @SuppressWarnings("unchecked") @Test public void testHttpUrlValidate() { - BasicValidationVTwo bv = new BasicValidationVTwo(Collections.EMPTY_MAP); + HttpServletRequest req = new HttpServletRequestStub(); + BasicValidationVTwo bv = new BasicValidationVTwo(Collections.EMPTY_MAP, I18nStub.bundle(req)); String res; res = bv.validate("httpUrl", "http://example.com/index"); - Assert.assertEquals(res, BasicValidationVTwo.SUCCESS); + Assert.assertEquals(BasicValidationVTwo.SUCCESS, res); res = bv.validate("httpUrl", "http://example.com/index?bogus=skjd%20skljd&something=sdkf"); - Assert.assertEquals(res, BasicValidationVTwo.SUCCESS); + Assert.assertEquals(BasicValidationVTwo.SUCCESS, res); res = bv.validate("httpUrl", "http://example.com/index#2.23?bogus=skjd%20skljd&something=sdkf"); - Assert.assertEquals(res, BasicValidationVTwo.SUCCESS); + Assert.assertEquals(BasicValidationVTwo.SUCCESS, res); } @SuppressWarnings("unchecked") @Test public void testEmptyValidate(){ - BasicValidationVTwo bv = new BasicValidationVTwo(Collections.EMPTY_MAP); + HttpServletRequest req = new HttpServletRequestStub(); + BasicValidationVTwo bv = new BasicValidationVTwo(Collections.EMPTY_MAP, I18nStub.bundle(req)); - Assert.assertEquals( - bv.validate("nonempty", null) - , BasicValidationVTwo.REQUIRED_FIELD_EMPTY_MSG); - - - Assert.assertEquals( - bv.validate("nonempty", "") - , BasicValidationVTwo.REQUIRED_FIELD_EMPTY_MSG); - - Assert.assertEquals( - bv.validate("nonempty", "some value") - , BasicValidationVTwo.SUCCESS); + Assert.assertEquals(BasicValidationVTwo.REQUIRED_FIELD_EMPTY_MSG, bv.validate("nonempty", null)); + Assert.assertEquals(BasicValidationVTwo.REQUIRED_FIELD_EMPTY_MSG, bv.validate("nonempty", "")); + Assert.assertEquals(BasicValidationVTwo.SUCCESS, bv.validate("nonempty", "some value")); } } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfFormTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfFormTest.java index 79ee54365..56a4b4d7c 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfFormTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/ProcessRdfFormTest.java @@ -19,11 +19,13 @@ import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants; +import stubs.javax.servlet.http.HttpServletRequestStub; -public class ProcessRdfFormTest extends AbstractTestClass{ +public class ProcessRdfFormTest extends AbstractTestClass { @Test public void basicNewStatementTest() throws Exception{ @@ -40,14 +42,16 @@ public class ProcessRdfFormTest extends AbstractTestClass{ values.put("test3", (new String[] {"http://test.com/uri3"})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); /* test just the N3 substitution part */ Listreq = config.getN3Required(); Listopt = config.getN3Optional(); - processor.subInValuesToN3( config , submission, req, opt, null , null); + processor.subInValuesToN3( config , submission, req, opt, null , null, vreq); assertNotNull(req); assertTrue( req.size() > 0); assertNotNull(req.get(0)); @@ -102,7 +106,10 @@ public class ProcessRdfFormTest extends AbstractTestClass{ Map values = new HashMap(); values.put("testZ", (new String[] {testZURIChanged})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); AdditionsAndRetractions changes = processor.process( config, submission, null ); @@ -176,14 +183,17 @@ public class ProcessRdfFormTest extends AbstractTestClass{ values.put("test2", (new String[] {test2})); values.put("test3", (new String[] {test3})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); /* test just the N3 substitution part */ Listreq = config.getN3Required(); Listopt = config.getN3Optional(); - processor.subInValuesToN3( config , submission, req, opt, null , null); + processor.subInValuesToN3( config , submission, req, opt, null , null, vreq); assertNotNull(req); assertTrue( req.size() > 0); assertNotNull(req.get(0)); @@ -220,14 +230,16 @@ public class ProcessRdfFormTest extends AbstractTestClass{ values.put("test3", (new String[] {"http://test.com/uri3"})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); /* test just the N3 substitution part */ Listreq = config.getN3Required(); Listopt = config.getN3Optional(); - processor.subInValuesToN3( config , submission, req, opt, null , null); + processor.subInValuesToN3( config , submission, req, opt, null , null, vreq); assertNotNull(req); assertTrue( req.size() > 0); assertNotNull(req.get(0)); @@ -270,14 +282,16 @@ public class ProcessRdfFormTest extends AbstractTestClass{ values.put("test3", (new String[] {"http://test.com/uri3"})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); /* test just the N3 substitution part */ Listreq = config.getN3Required(); Listopt = config.getN3Optional(); - processor.subInValuesToN3( config , submission, req, opt, null , null); + processor.subInValuesToN3( config , submission, req, opt, null , null, vreq); assertNotNull(req); assertTrue( req.size() > 0); assertNotNull(req.get(0)); @@ -357,10 +371,13 @@ public class ProcessRdfFormTest extends AbstractTestClass{ values.put("testZ", (new String[] {testZURIChanged})); values.put("zLabel", (new String[] {"New Z Label"})); values.put("editKey", (new String[] {"mockEditKey"})); - MultiValueEditSubmission submission = new MultiValueEditSubmission(values, config); + + VitroRequest vreq = createRequestWithParameters(values); + + MultiValueEditSubmission submission = new MultiValueEditSubmission(vreq, config); ProcessRdfForm processor = new ProcessRdfForm(config,getMockNewURIMaker()); - AdditionsAndRetractions changes = processor.process( config, submission, null ); + AdditionsAndRetractions changes = processor.process( config, submission, vreq ); assertNotNull( changes ); assertNotNull( changes.getAdditions() ); @@ -396,4 +413,16 @@ public class ProcessRdfFormTest extends AbstractTestClass{ } }; } + + private VitroRequest createRequestWithParameters(Map parameters) { + HttpServletRequestStub req = new HttpServletRequestStub(); + for (String key : parameters.keySet()) { + for (String value : parameters.get(key)) { + req.addParameter(key, value); + } + } + + return new VitroRequest(req); + } + } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguageTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguageTest.java new file mode 100644 index 000000000..d1f6b8433 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/LimitRemovalsToLanguageTest.java @@ -0,0 +1,62 @@ +package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors; + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.vocabulary.RDFS; +import org.junit.Test; +import org.testng.Assert; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +public class LimitRemovalsToLanguageTest extends AbstractTestClass { + + @Test + /** + * Test that retractions are properly limited to the specified language + */ + public void testPreprocess() { + LimitRemovalsToLanguage preproc = new LimitRemovalsToLanguage("en-US"); + Model additions = ModelFactory.createDefaultModel(); + Model retractions = ModelFactory.createDefaultModel(); + Resource res = ResourceFactory.createResource("http://example.com/i/n1"); + // eliminate Spanish retraction if only English is being edited + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US")); + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("es1", "es")); + additions.add(res, RDFS.label, ResourceFactory.createLangLiteral("en-US2", "en-US")); + preproc.preprocess(retractions, additions, null); + Assert.assertEquals(retractions.size(), 1); + Assert.assertTrue(retractions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US"))); + Assert.assertEquals(additions.size(), 1); + Assert.assertTrue(additions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("en-US2", "en-US"))); + additions.removeAll(); + retractions.removeAll(); + // Keep all retractions unmolested if no labels at all are being re-added. + // (The form may be trying to delete the entire individual.) + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US")); + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("es1", "es")); + preproc.preprocess(retractions, additions, null); + Assert.assertEquals(retractions.size(), 2); + Assert.assertTrue(retractions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US"))); + Assert.assertTrue(retractions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("es1", "es"))); + Assert.assertEquals(additions.size(), 0); + additions.removeAll(); + retractions.removeAll(); + // Keep both retractions if the form supplies new values for both languages + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US")); + retractions.add(res, RDFS.label, ResourceFactory.createLangLiteral("es1", "es")); + additions.add(res, RDFS.label, ResourceFactory.createLangLiteral("en-US2", "en-US")); + additions.add(res, RDFS.label, ResourceFactory.createLangLiteral("es2", "es")); + preproc.preprocess(retractions, additions, null); + Assert.assertEquals(retractions.size(), 2); + Assert.assertTrue(retractions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("en-US1", "en-US"))); + Assert.assertTrue(retractions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("es1", "es"))); + Assert.assertEquals(additions.size(), 2); + Assert.assertTrue(additions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("en-US2", "en-US"))); + Assert.assertTrue(additions.contains(res, RDFS.label, ResourceFactory.createLangLiteral("es2", "es"))); + additions.removeAll(); + retractions.removeAll(); + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidationTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidationTest.java index e9e2c463d..3c238facd 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidationTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidationTest.java @@ -9,10 +9,13 @@ import java.util.Map; import org.junit.Assert; import org.junit.Test; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.MultiValueEditSubmission; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; +import stubs.javax.servlet.http.HttpServletRequestStub; + public class AntiXssValidationTest { @Test @@ -29,8 +32,10 @@ public class AntiXssValidationTest { String[] vals= { "some sort of string" }; params.put("X", vals); + VitroRequest vreq = createRequestWithParameters(params); + MultiValueEditSubmission mvEditSub = - new MultiValueEditSubmission(params,eConf); + new MultiValueEditSubmission(vreq,eConf); Map res = validator.validate(eConf, mvEditSub); Assert.assertEquals(null, res); @@ -53,8 +58,10 @@ public class AntiXssValidationTest { String[] strings2 = {"no problem 2"}; params.put("Z", strings2 ); + VitroRequest vreq = createRequestWithParameters(params); + MultiValueEditSubmission mvEditSub = - new MultiValueEditSubmission(params,eConf); + new MultiValueEditSubmission(vreq,eConf); Map res = validator.validate(eConf, mvEditSub); Assert.assertNull( res ); @@ -72,8 +79,10 @@ public class AntiXssValidationTest { Map params = new HashMap(); params.put("X", strings ); + VitroRequest vreq = createRequestWithParameters(params); + MultiValueEditSubmission mvEditSub = - new MultiValueEditSubmission(params,eConf); + new MultiValueEditSubmission(vreq,eConf); return validator.validate(eConf, mvEditSub); } @@ -125,5 +134,15 @@ public class AntiXssValidationTest { Assert.assertNotNull(result); } + private VitroRequest createRequestWithParameters(Map parameters) { + HttpServletRequestStub req = new HttpServletRequestStub(); + for (String key : parameters.keySet()) { + for (String value : parameters.get(key)) { + req.addParameter(key, value); + } + } + + return new VitroRequest(req); + } } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFServiceTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFServiceTest.java index 261718d6d..a062798f1 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFServiceTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFServiceTest.java @@ -282,12 +282,12 @@ public class LanguageFilteringRDFServiceTest extends AbstractTestClass { private Comparator buildRowIndexedLiteralSortByLang() { try { Class clazz = Class.forName(COLLATOR_CLASSNAME); - Class[] argTypes = { LanguageFilteringRDFService.class }; - Constructor constructor = clazz.getDeclaredConstructor(argTypes); + Class[] argTypes = { LanguageFilteringRDFService.class, List.class }; + Constructor constructor = clazz.getDeclaredConstructor(argTypes); constructor.setAccessible(true); - return (Comparator) constructor - .newInstance(filteringRDFService); + .newInstance(filteringRDFService, new AcceptableLanguages( + preferredLanguages)); } catch (Exception e) { throw new RuntimeException("Could not create a collator", e); } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/ThumbnailImageURLTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/ThumbnailImageURLTest.java index cd33c59f6..9f304f998 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/ThumbnailImageURLTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/ThumbnailImageURLTest.java @@ -37,7 +37,7 @@ public class ThumbnailImageURLTest extends AbstractTestClass{ * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { setLoggerLevel(RDFDefaultErrorHandler.class, Level.OFF); ApplicationStub.setup(new ServletContextStub(), new SearchEngineStub()); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectPropertiesTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectPropertiesTest.java index 038c25453..7257f1ad7 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectPropertiesTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/AdditionalURIsForObjectPropertiesTest.java @@ -90,13 +90,14 @@ public class AdditionalURIsForObjectPropertiesTest { Assert.assertTrue("uris was empty", uris.size() > 0 ); Assert.assertTrue("uris didn't not contain test:cheese", uris.contains(testNS+"cheese")); + Assert.assertTrue("uris didn't not contain test:bob", uris.contains(testNS+"bob")); Assert.assertTrue("uris contained test:Person", !uris.contains(testNS+"Person")); Assert.assertTrue("uris contained owl:Thing", !uris.contains( OWL.Thing.getURI() )); Assert.assertTrue("uris contained test:onions", !uris.contains(testNS+"onions")); Assert.assertTrue("uris contained test:icecream", !uris.contains(testNS+"icecream")); - Assert.assertEquals(1, uris.size()); + Assert.assertEquals(2, uris.size()); } @Test diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java index 5906724f6..4dd63f99d 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java @@ -32,7 +32,7 @@ public class DataGetterUtilsTest extends AbstractTestClass{ String dataGetterX = "http://vitro.mannlib.cornell.edu/ontologies/display/1.1#pageDataGetterX"; @Before - public void setUp() throws Exception { + public void setUp() { // Suppress error logging. setLoggerLevel(RDFDefaultErrorHandler.class, Level.OFF); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetterTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetterTest.java index 8cb9279ef..a02516e1d 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetterTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetterTest.java @@ -36,7 +36,7 @@ public class SparqlQueryDataGetterTest extends AbstractTestClass{ VitroRequest vreq; @Before - public void setUp() throws Exception { + public void setUp() { // Suppress error logging. setLoggerLevel(RDFDefaultErrorHandler.class, Level.OFF); diff --git a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryStub.java b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryStub.java index 24efa6885..732ae3209 100644 --- a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryStub.java +++ b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/dao/WebappDaoFactoryStub.java @@ -23,6 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; +import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; /** * A minimal implementation of the WebappDaoFactory. @@ -267,4 +268,9 @@ public class WebappDaoFactoryStub implements WebappDaoFactory { throw new RuntimeException("WebappDaoFactory.close() not implemented."); } + @Override + public I18nBundle getI18nBundle() { + throw new RuntimeException("WebappDaoFactory.getI18nBundle() not implemented."); + } + } diff --git a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/i18n/I18nStub.java b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/i18n/I18nStub.java index 76d3a3727..372eedc69 100644 --- a/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/i18n/I18nStub.java +++ b/api/src/test/java/stubs/edu/cornell/mannlib/vitro/webapp/i18n/I18nStub.java @@ -14,6 +14,7 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle; +import stubs.javax.servlet.ServletContextStub; /** * An implementation of I18n for unit tests. Construct a new instance and it @@ -45,6 +46,7 @@ public class I18nStub extends I18n { /** Make it private, so they will use the setup() method. */ private I18nStub() { + super(new ServletContextStub()); // Nothing to initialize. } diff --git a/api/src/test/java/stubs/javax/servlet/http/HttpServletRequestStub.java b/api/src/test/java/stubs/javax/servlet/http/HttpServletRequestStub.java index 27bb5e69f..35b8da99c 100644 --- a/api/src/test/java/stubs/javax/servlet/http/HttpServletRequestStub.java +++ b/api/src/test/java/stubs/javax/servlet/http/HttpServletRequestStub.java @@ -510,8 +510,7 @@ public class HttpServletRequestStub implements HttpServletRequest { @Override public Locale getLocale() { - throw new RuntimeException( - "HttpServletRequestStub.getLocale() not implemented."); + return Locale.ENGLISH; } @Override diff --git a/api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java b/api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java index 56142fc7a..fac169042 100644 --- a/api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java +++ b/api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java @@ -44,6 +44,11 @@ public class LiteralStub implements Literal { return false; } + @Override + public boolean isStmtResource() { + return false; + } + @Override public boolean isURIResource() { return false; diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 01059dcf3..078740a75 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-dependencies - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom org.vivoweb vitro-project - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. @@ -176,7 +176,7 @@ org.apache.jena jena-arq - 3.11.0 + 3.16.0 @@ -192,23 +192,33 @@ org.apache.jena jena-core - 3.11.0 + 3.16.0 org.apache.jena jena-sdb - 3.11.0 + 3.16.0 org.apache.jena jena-tdb - 3.11.0 + 3.16.0 org.apache.solr solr-solrj 7.4.0 + + commons-codec + commons-codec + 1.15 + + + org.slf4j + slf4j-log4j12 + 1.7.26 + org.directwebremoting dwr diff --git a/home/pom.xml b/home/pom.xml index fdc0237f2..dc31e4e4f 100644 --- a/home/pom.xml +++ b/home/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-home - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom org.vivoweb vitro-project - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. diff --git a/home/src/main/resources/config/example.applicationSetup.n3 b/home/src/main/resources/config/example.applicationSetup.n3 index 5d0562be2..0df7bdb6e 100644 --- a/home/src/main/resources/config/example.applicationSetup.n3 +++ b/home/src/main/resources/config/example.applicationSetup.n3 @@ -26,7 +26,7 @@ :hasSearchIndexer :basicSearchIndexer ; :hasImageProcessor :iioImageProcessor ; :hasFileStorage :ptiFileStorage ; - :hasContentTripleSource :sdbContentTripleSource ; + :hasContentTripleSource :tdbContentTripleSource ; :hasConfigurationTripleSource :tdbConfigurationTripleSource ; :hasTBoxReasonerModule :jfactTBoxReasonerModule . @@ -82,22 +82,22 @@ # ---------------------------- # # Content triples source module: holds data contents -# The SDB-based implementation is the default option. It reads its parameters +# The TDB-based implementation is the default option. It reads its parameters # from the runtime.properties file, for backward compatibility. # -# Other implementations are based on a local TDB instance, a "standard" SPARQL +# Other implementations are based on an SDB instance, a "standard" SPARQL # endpoint, or a Virtuoso endpoint, with parameters as shown. # -:sdbContentTripleSource - a vitroWebapp:triplesource.impl.sdb.ContentTripleSourceSDB , - vitroWebapp:modules.tripleSource.ContentTripleSource . +#:sdbContentTripleSource +# a vitroWebapp:triplesource.impl.sdb.ContentTripleSourceSDB , +# vitroWebapp:modules.tripleSource.ContentTripleSource . -#:tdbContentTripleSource -# a vitroWebapp:triplesource.impl.tdb.ContentTripleSourceTDB , -# vitroWebapp:modules.tripleSource.ContentTripleSource ; -# # May be an absolute path, or relative to the Vitro home directory. -# :hasTdbDirectory "tdbContentModels" . +:tdbContentTripleSource + a vitroWebapp:triplesource.impl.tdb.ContentTripleSourceTDB , + vitroWebapp:modules.tripleSource.ContentTripleSource ; + # May be an absolute path, or relative to the Vitro home directory. + :hasTdbDirectory "tdbContentModels" . #:sparqlContentTripleSource # a vitroWebapp:triplesource.impl.virtuoso.ContentTripleSourceSPARQL , diff --git a/home/src/main/resources/config/example.developer.properties b/home/src/main/resources/config/example.developer.properties index 67b119f92..7c5ab1ba4 100644 --- a/home/src/main/resources/config/example.developer.properties +++ b/home/src/main/resources/config/example.developer.properties @@ -49,6 +49,7 @@ # developer.i18n.defeatCache = false # developer.i18n.logStringRequests = false +# developer.i18n.onlineTranslation = false #------------------------------------------------------------------------------ diff --git a/home/src/main/resources/config/example.runtime.properties b/home/src/main/resources/config/example.runtime.properties index a0bf89a4f..8e287ca0e 100644 --- a/home/src/main/resources/config/example.runtime.properties +++ b/home/src/main/resources/config/example.runtime.properties @@ -24,51 +24,70 @@ Vitro.defaultNamespace = http://vivo.mydomain.edu/individual/ # # URL of Solr context used in local Vitro search. This will usually consist of: -# scheme + server_name + port + vitro_webapp_name + "solr" -# In the standard installation, the Solr context will be on the same server as Vitro, -# and in the same Tomcat instance. The path will be the Vitro webapp.name (specified -# above) + "solr" +# scheme + server_name + port + "solr" + solr_core_name +# In a standard Solr installation, the Solr service will be available on port +# 8983. The path will be /solr followed by the name used when adding a core +# for Vitro. # Example: -# vitro.local.solr.url = http://localhost:8080/vitrosolr -vitro.local.solr.url = http://localhost:8080/vitrosolr +# vitro.local.solr.url = http://localhost:8983/solr/vitrocore +# +vitro.local.solr.url = http://localhost:8983/solr/vitrocore # # Email parameters which VIVO can use to send mail. If these are left empty, # the "Contact Us" form will be disabled and users will not be notified of # changes to their accounts. -# -email.smtpHost = smtp.my.domain.edu -email.replyTo = vivoAdmin@my.domain.edu + # Example: + # email.smtpHost = smtp.mydomain.edu + # email.replyTo = vitroAdmin@mydomain.edu + # +email.smtpHost = +email.replyTo = # -# The basic parameters for a MySQL database connection. Change the end of the -# URL to reflect your database name (if it is not "vitro"). Change the username -# and password to match the authorized user you created in MySQL. +# NOTE: VitroConnection.DataSource.* properties are only used in conjuction with +# an SDB triple store. # -VitroConnection.DataSource.url = jdbc:mysql://localhost/vitro -VitroConnection.DataSource.username = vitroweb -VitroConnection.DataSource.password = vitrovitro +# The basic parameters for a database connection. Change the end of the +# URL to reflect your database name (if it is not "vitrodb"). Change the username +# and password to match the authorized database user you created. +# +# VitroConnection.DataSource.url = jdbc:mysql://localhost/vitrodb +# VitroConnection.DataSource.username = vitrodbUsername +# VitroConnection.DataSource.password = vitrodbPassword # # The maximum number of active connections in the database connection pool. # Increase this value to support a greater number of concurrent page requests. # -VitroConnection.DataSource.pool.maxActive = 40 +# VitroConnection.DataSource.pool.maxActive = 40 # # The maximum number of database connections that will be allowed # to remain idle in the connection pool. Default is 25% # of the maximum number of active connections. # -VitroConnection.DataSource.pool.maxIdle = 10 +# VitroConnection.DataSource.pool.maxIdle = 10 # -# Parameters to change in order to use VIVO with a database other than +# Parameters to change in order to use Vitro with a database other than # MySQL. # -VitroConnection.DataSource.dbtype = MySQL -VitroConnection.DataSource.driver = com.mysql.jdbc.Driver -VitroConnection.DataSource.validationQuery = SELECT 1 +# VitroConnection.DataSource.dbtype = MySQL +# VitroConnection.DataSource.driver = com.mysql.jdbc.Driver +# VitroConnection.DataSource.validationQuery = SELECT 1 + +# +# Include sections between +# tags when executing 'list view' queries that retrieve data +# for property lists on profile pages. +# +# Including these optional sections does not change the query +# semantics, but may improve performance. +# +# Default is true if not set. +# +# listview.usePreciseSubquery = true # # The email address of the root user for the VIVO application. The password @@ -144,9 +163,9 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing # # A list of supported languages or Locales that the user may choose to -# use instead of the one specified by the browser. Selection images must -# be available in the i18n/images directory of the theme. This affects -# RDF data retrieved from the model, if RDFService.languageFilter is true. +# use instead of the one specified by the browser. The selected language(s) +# must exist in the Vitro-languages repository. This affects RDF data +# retrieved from the model, if RDFService.languageFilter is true. # This also affects the text of pages that have been modified to support # multiple languages. # @@ -154,7 +173,15 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing # # languages.selectableLocales = en, es, fr -# Triple pattern fragments is a very fast, very simple means for querying a triple store. -# The triple pattern fragments API in VIVO puts little load on the server, providing a simple means for getting data from the triple store. The API has a web interface for manual use, can be used from the command line via curl, and can be used by programs. - +# Triple Pattern Fragments is a very fast, very simple means for querying a +# triple store. The Triple Pattern Fragments API in VIVO puts little load on +# the server, providing a simple means for getting data from the triple store. +# The API has a web interface for manual use, can be used from the command line +# via curl, and can be used by programs. +# +# Vitro's Triple Pattern Fragments API does not require authentication and +# makes the full RDF graph available regardless of display or publish levels +# set on particular properties. Enable Triple Pattern Fragments only if your +# Vitro does not contain restricted data that should not be shared with others. +# # tpf.activeFlag = true diff --git a/installer/home/pom.xml b/installer/home/pom.xml index be8bb3023..83f2dc8fe 100644 --- a/installer/home/pom.xml +++ b/installer/home/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-home - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom org.vivoweb vitro-installer - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. diff --git a/installer/pom.xml b/installer/pom.xml index 0323adcf4..68dd1dbd7 100644 --- a/installer/pom.xml +++ b/installer/pom.xml @@ -7,9 +7,16 @@ org.vivoweb vitro-installer - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom + + org.vivoweb + vitro-project + 1.12.0-SNAPSHOT + .. + + Vitro Installer diff --git a/installer/solr/pom.xml b/installer/solr/pom.xml index ae67209fd..58903929a 100644 --- a/installer/solr/pom.xml +++ b/installer/solr/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-solr - 1.11.0-SNAPSHOT + 1.12.0-SNAPSHOT war org.vivoweb vitro-installer - 1.11.0-SNAPSHOT + 1.12.0-SNAPSHOT .. diff --git a/installer/webapp/pom.xml b/installer/webapp/pom.xml index a17179d14..69abcc2a8 100644 --- a/installer/webapp/pom.xml +++ b/installer/webapp/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-webapp - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT war org.vivoweb vitro-installer - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. @@ -45,11 +45,11 @@ war - + @@ -148,12 +148,12 @@ war - + javax.servlet diff --git a/installer/webapp/src/main/webResources/META-INF/context.xml b/installer/webapp/src/main/webResources/META-INF/context.xml index 1f41e94bf..53ea22ae5 100644 --- a/installer/webapp/src/main/webResources/META-INF/context.xml +++ b/installer/webapp/src/main/webResources/META-INF/context.xml @@ -1,8 +1,8 @@ + type="java.lang.String" + name="vitro/home" + value="${vitro-dir}" override="true"/> diff --git a/pom.xml b/pom.xml index 13d2c8f1b..c0775cf69 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.vivoweb vitro-project - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT pom Vitro diff --git a/webapp/pom.xml b/webapp/pom.xml index a9b1a8fe5..4e66b0998 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-webapp - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT war org.vivoweb vitro-project - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT .. @@ -41,7 +41,7 @@ org.vivoweb vitro-api - 1.11.2-SNAPSHOT + 1.12.0-SNAPSHOT diff --git a/webapp/src/main/webapp/css/vitro.css b/webapp/src/main/webapp/css/vitro.css index 8bafa5922..f784acd7f 100644 --- a/webapp/src/main/webapp/css/vitro.css +++ b/webapp/src/main/webapp/css/vitro.css @@ -252,10 +252,11 @@ ul.dropdown li:hover > ul { /* -------------------------------------------------> */ ul.language-dropdown { position: relative; + min-width: 160px; } ul.language-dropdown li#language-menu { background: url(../images/arrowDownOverAccount.gif) right 9px no-repeat; - min-width:110px; + min-width: 170px; position: relative; margin-right: -10px !important; } diff --git a/webapp/src/main/webapp/i18n/all.properties b/webapp/src/main/webapp/i18n/all.properties deleted file mode 100644 index 1909545ad..000000000 --- a/webapp/src/main/webapp/i18n/all.properties +++ /dev/null @@ -1,928 +0,0 @@ -# -# Text strings for the controllers and templates -# -# Default (English) -# -save_changes=Save changes -save_entry=Save entry -select_existing=Select existing -select_an_existing=Select an existing -add_an_entry_to=Add an entry of type -change_entry_for=Change entry for: -add_new_entry_for=Add new entry for: -change_text_for=Change text for: -cancel_link = Cancel -cancel_title = cancel -required_fields = required fields -or = or -alt_error_alert = Error alert icon -alt_confirmation = Confirmation icon -for=for -email_address = Email address -first_name = First name -last_name = Last name -roles = Roles -status = Status - -ascending_order = ascending order -descending_order = descending order -select_one = Select one - -type_more_characters = type more characters -no_match = no match - -request_failed = Request failed. Please contact your system administrator. - -# -# Image upload pages -# -upload_page_title = Upload image -upload_page_title_with_name = Upload image for {0} -upload_heading = Photo Upload - -replace_page_title = Replace image -replace_page_title_with_name = Replace image for {0} - -crop_page_title = Crop image -crop_page_title_with_name = Crop image for {0} - -current_photo = Current Photo -upload_photo = Upload a photo -replace_photo = Replace Photo -photo_types = (JPEG, GIF or PNG) -maximum_file_size = Maximum file size: {0} megabytes -minimum_image_dimensions = Minimum image dimensions: {0} x {1} pixels - -cropping_caption = Your profile photo will look like the image below. -cropping_note = To make adjustments, you can drag around and resize the photo to the right. \ -When you are happy with your photo click the "Save Photo" button. - -alt_thumbnail_photo = Individual photo -alt_image_to_crop = Image to be cropped -alt_preview_crop = Preview of photo cropped - -delete_link = Delete photo -submit_upload = Upload photo -submit_save = Save photo - -confirm_delete = Are you sure you want to delete this photo? - -imageUpload.errorNoURI = No entity URI was provided -imageUpload.errorUnrecognizedURI = This URI is not recognized as belonging to anyone: ''{0}'' -imageUpload.errorNoImageForCropping = There is no image file to be cropped. -imageUpload.errorImageTooSmall = The uploaded image should be at least {0} pixels high and {1} pixels wide. -imageUpload.errorUnknown = Sorry, we were unable to process the photo you provided. Please try another photo. -imageUpload.errorFileTooBig = Please upload an image smaller than {0} megabytes. -imageUpload.errorUnrecognizedFileType = ''{0}'' is not a recognized image file type. Please upload JPEG, GIF, or PNG files only. -imageUpload.errorNoPhotoSelected = Please browse and select a photo. -imageUpload.errorBadMultipartRequest = Failed to parse the multi-part request for uploading an image. -imageUpload.errorFormFieldMissing = The form did not contain a ''{0}'' field." - -# -# User Accounts pages -# -account_management = Account Management -user_accounts_link = User accounts -user_accounts_title = user accounts - -login_count = Login count -last_login = Last Login - -add_new_account = Add new account -edit_account = Edit account -external_auth_only = Externally Authenticated Only -reset_password = Reset password -reset_password_note = Note: Instructions for resetting the password will \ -be emailed to the address entered above. The password will not \ -be reset until the user follows the link provided in this email. -new_password = New password -confirm_password = Confirm new password -minimum_password_length = Minimum of {0} characters in length; maximum of {1}. -leave_password_unchanged = Leaving this blank means that the password will not be changed. -confirm_initial_password = Confirm initial password - -new_account_1 = A new account for -new_account_2 = was successfully created. -new_account_title = new account -new_account_notification = A notification email has been sent to {0} \ -with instructions for activating the account and creating a password. -updated_account_1 = The account for -updated_account_2 = has been updated. -updated_account_title = updated account -updated_account_notification = A confirmation email has been sent to {0} \ -with instructions for resetting a password. \ -The password will not be reset until the user follows the link provided in this email. -deleted_accounts = Deleted {0} {0, choice, 0#accounts|1#account|1= 200 && xhr.status <= 299; + } // `a.click()` doesn't work for all browsers (#465) + + + function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')); + } catch (e) { + var evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); + node.dispatchEvent(evt); + } + } // Detect WebView inside a native macOS app by ruling out all browsers + // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too + // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos + + + var isMacOSWebView = /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent); + var saveAs = _global.saveAs || ( // probably in some web worker + typeof window !== 'object' || window !== _global ? function saveAs() {} + /* noop */ + // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView + : 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) { + var URL = _global.URL || _global.webkitURL; + var a = document.createElement('a'); + name = name || blob.name || 'download'; + a.download = name; + a.rel = 'noopener'; // tabnabbing + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + + if (typeof blob === 'string') { + // Support regular links + a.href = blob; + + if (a.origin !== location.origin) { + corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank'); + } else { + click(a); + } + } else { + // Support blobs + a.href = URL.createObjectURL(blob); + setTimeout(function () { + URL.revokeObjectURL(a.href); + }, 4E4); // 40s + + setTimeout(function () { + click(a); + }, 0); + } + } // Use msSaveOrOpenBlob as a second approach + : 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) { + name = name || blob.name || 'download'; + + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts); + } else { + var a = document.createElement('a'); + a.href = blob; + a.target = '_blank'; + setTimeout(function () { + click(a); + }); + } + } else { + navigator.msSaveOrOpenBlob(bom(blob, opts), name); + } + } // Fallback to using FileReader and a popup + : function saveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank'); + + if (popup) { + popup.document.title = popup.document.body.innerText = 'downloading...'; + } + + if (typeof blob === 'string') return download(blob, name, opts); + var force = blob.type === 'application/octet-stream'; + + var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari; + + var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); + + if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') { + // Safari doesn't allow downloading of blob URLs + var reader = new FileReader(); + + reader.onloadend = function () { + var url = reader.result; + url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); + if (popup) popup.location.href = url;else location = url; + popup = null; // reverse-tabnabbing #460 + }; + + reader.readAsDataURL(blob); + } else { + var URL = _global.URL || _global.webkitURL; + var url = URL.createObjectURL(blob); + if (popup) popup.location = url;else location.href = url; + popup = null; // reverse-tabnabbing #460 + + setTimeout(function () { + URL.revokeObjectURL(url); + }, 4E4); // 40s + } + }); + _global.saveAs = saveAs.saveAs = saveAs; + + if (typeof module !== 'undefined') { + module.exports = saveAs; + } +}); diff --git a/webapp/src/main/webapp/js/developer/developerPanel.js b/webapp/src/main/webapp/js/developer/developerPanel.js index b92f2abdf..d8cf42b47 100644 --- a/webapp/src/main/webapp/js/developer/developerPanel.js +++ b/webapp/src/main/webapp/js/developer/developerPanel.js @@ -52,6 +52,7 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developer_pageContents_logCustomShortView").disabled = !developerEnabled; document.getElementById("developer_i18n_defeatCache").disabled = !developerEnabled; document.getElementById("developer_i18n_logStringRequests").disabled = !developerEnabled; + document.getElementById("developer_i18n_onlineTranslation").disabled = !developerEnabled; document.getElementById("developer_loggingRDFService_enable").disabled = !developerEnabled; document.getElementById("developer_searchIndex_enable").disabled = !developerEnabled; document.getElementById("developer_searchIndex_logIndexingBreakdownTimings").disabled = !developerEnabled; diff --git a/webapp/src/main/webapp/js/developer/translations.js b/webapp/src/main/webapp/js/developer/translations.js new file mode 100644 index 000000000..8fd9075c2 --- /dev/null +++ b/webapp/src/main/webapp/js/developer/translations.js @@ -0,0 +1,484 @@ +class PropAddr { + constructor(node, number, args) { + this.node = node; + this.number = number; + this.args = args; + } +} + +class PropInfo { + constructor(rawText, formText, address) { + this.rawText = rawText; + this.formText = formText; + this.addresses = []; + this.addresses.push(address); + } +} + + var pageTranslations = new Map(); + var overridenTranslations = new Map(); + var startSep = '\u25a4'; + var endSep = '\u25a5'; + var intSep = '\u25a6'; + var resultSep = '\u200b\uFEFF\u200b\uFEFF\u200b'; + var resultSepChars = '\u200b\uFEFF'; + + function saveTranslations() { + var storage = window.localStorage; + var serializedTranslations = JSON.stringify(Array.from(overridenTranslations.entries())); + storage.setItem("overridenTranslations", serializedTranslations); + } + + function readTranslations() { + var storage = window.localStorage; + var serializedTranslations = storage.getItem("overridenTranslations"); + if (serializedTranslations != null) { + overridenTranslations = new Map(JSON.parse(serializedTranslations)); + } + } + + function createTranslationPanel() { + var devPanel = document.getElementById("developerPanel"); + if (devPanel !== null) { + var container = document.createElement("div"); + container.setAttribute("id", "translationPanel"); + container.setAttribute("style", "font-size:0.8em !important;width: 440px; resize: horizontal; \ + overflow: auto; padding: 10px; position: absolute;background-color:#f7dd8a;border:1px dotted;z-index:10000"); + devPanel.parentNode.insertBefore(container, devPanel.nextSibling); + createTranslationControls(container); + createPageTranslationsTable(container); + } + } + + function createTranslationControls(container) { + var controls = document.createElement("div"); + controls.setAttribute("id", "translationControls"); + controls.setAttribute("style", "margin-bottom:8px;") + container.appendChild(controls); + + var cleanButton = document.createElement("button"); + cleanButton.textContent = "Clean All"; + cleanButton.setAttribute("onclick", "cleanTranslationStorage()"); + cleanButton.setAttribute("style", "margin-right:10px;"); + controls.appendChild(cleanButton); + + var exportAllButton = document.createElement("button"); + exportAllButton.textContent = "Export All"; + exportAllButton.setAttribute("onclick", "exportTranslations()"); + exportAllButton.setAttribute("style", "margin-right:10px;"); + controls.appendChild(exportAllButton); + + var updateFileInput = document.createElement("input"); + var updateFileButton = document.createElement("button"); + updateFileButton.setAttribute("style", "margin-right:10px;"); + updateFileInput.type = "file"; + updateFileInput.setAttribute("id", "exportFile"); + updateFileInput.setAttribute("style", "display:none;"); + updateFileInput.setAttribute("accept", ".properties"); + var updateFileLabel = document.createElement("label"); + updateFileLabel.setAttribute("for", "exportFile"); + updateFileLabel.textContent = "Update file"; + updateFileLabel.setAttribute("style", "margin:0px;color:black;") + updateFileButton.appendChild(updateFileLabel); + controls.appendChild(updateFileButton); + controls.appendChild(updateFileInput); + updateFileInput.addEventListener("change", updateTranslationsFile); + + var importFileInput = document.createElement("input"); + var importFileButton = document.createElement("button"); + importFileInput.type = "file"; + importFileInput.setAttribute("style", "display:none;"); + importFileInput.setAttribute("id", "importFile"); + importFileInput.setAttribute("accept", ".properties"); + var importFileLabel = document.createElement("label"); + importFileLabel.setAttribute("style", "margin:0px;color:black;") + importFileLabel.setAttribute("for", "importFile"); + importFileLabel.textContent = "Import from file"; + importFileButton.appendChild(importFileLabel); + controls.appendChild(importFileButton); + controls.appendChild(importFileInput); + importFileInput.addEventListener("change", importTranslationsFromFile); + } + + function cleanTranslationStorage() { + overridenTranslations.clear(); + saveTranslations(); + location.reload(); + } + + function importTranslationsFromFile(e) { + const fileList = e.target.files; + const numFiles = fileList.length; + if (numFiles > 0) { + const file = fileList[0]; + var reader = new FileReader(); + reader.onload = function(progressEvent) { + var lines = this.result.split(/\r\n|\n\r|\n|\r/); + var followLine = false; + var lineKey = null; + for (var i = 0; i < lines.length; i++) { + if (!isCommentLine(lines[i])) { + if (followLine) { + followLine = isNextLineFollow(lines[i]); + var lineValue = lines[i].replace(/\\$/, ""); + overridenTranslations.set(lineKey, overridenTranslations.get(lineKey) + unescapeHTML(lineValue)); + } else { + followLine = isNextLineFollow(lines[i]); + lineKey = getLineKey(lines[i]); + if (lineKey.trim() != "") { + let lineValue = getLineValue(lines[i]); + overridenTranslations.set(lineKey, unescapeHTML(lineValue)); + } + } + } + } + saveTranslations(); + location.reload() + } + reader.readAsText(file); + } + } + + function updateTranslationsFile(e) { + const fileList = e.target.files; + const numFiles = fileList.length; + if (numFiles > 0) { + const file = fileList[0]; + var fileName = e.target.value.split(/(\\|\/)/g).pop(); + var reader = new FileReader(); + reader.onload = function(progressEvent) { + var lines = this.result.split(/\r\n|\n\r|\n|\r/); + var followLine = false; + var keyLineHasChanged = false; + var lineKey = null; + for (var i = 0; i < lines.length; i++) { + if (!isCommentLine(lines[i])) { + if (followLine) { + followLine = isNextLineFollow(lines[i]); + if (keyLineHasChanged) { + //clean line as it's upper content has changed + lines[i] = ""; + if (!followLine) { + keyLineHasChanged = false; + } + } + // skip line + } else { + keyLineHasChanged = false; + followLine = isNextLineFollow(lines[i]); + lineKey = getLineKey(lines[i]); + if (overridenTranslations.has(lineKey)) { + var value = overridenTranslations.get(lineKey); + lines[i] = lineKey + " = " + value; + keyLineHasChanged = true; + } + } + } + } + saveFile(fileName, lines); + } + reader.readAsText(file); + } + } + + function exportTranslations() { + var date = new Date; + var fileName = "export_" + date.toLocaleString() + "_all.properties"; + var lines = []; + for (let [key, value] of overridenTranslations) { + lines.push(key + " = " + escapeHTML(value)); + } + saveFile(fileName, lines); + } + + function saveFile(fileName, lines) { + var blob = new Blob([lines.join("\n")], { type: 'text/plain;charset=utf-8' }); + saveAs(blob, fileName); + } + + function getLineKey(line) { + var matches = line.match(/^\s*[^=\s]*(?=\s*=)/); + var key; + if (matches == null) { + key = ""; + } else { + key = matches[0].trim(); + } + return key; + } + + function getLineValue(line) { + var value = line.replace(/^\s*[^=\s]*\s*=\s*/, ""); + value = value.replace(/\\$/, ""); + return value; + } + + function isNextLineFollow(line) { + return line.match(/\\(\\\\)*$/) != null; + } + + function isCommentLine(line) { + return line.match(/^\s*[#!]/) != null; + } + + function createPageTranslationsTable(container) { + var table = document.createElement("table"); + table.setAttribute("id", "translationsTable"); + table.setAttribute("style", "width:100%;"); + + document.getElementById("translationPanel").appendChild(table); + for (let [key, propInfo] of pageTranslations) { + var tr = document.createElement("tr"); + table.appendChild(tr); + var td1 = document.createElement("td"); + td1.setAttribute("style", " width:1%;white-space:nowrap;"); + var keyText = document.createTextNode(key); + var td2 = document.createElement("td"); + var rawText = document.createElement("input"); + rawText.setAttribute("style", "width:100%; "); + if (overridenTranslations.has(key)) { + rawText.value = overridenTranslations.get(key); + rawText.style.backgroundColor = "#8BAB2E"; + } else { + rawText.value = propInfo.rawText; + } + var rawTextHidden = document.createElement("input"); + rawTextHidden.setAttribute("style", "display:none;"); + rawTextHidden.value = propInfo.rawText; + td1.appendChild(keyText); + tr.appendChild(td1); + td2.appendChild(rawText); + td2.appendChild(rawTextHidden); + tr.appendChild(td2); + rawText.addEventListener("blur", function() { + updateTranslation(this); + }); + } + } + + function updateTranslation(input) { + if (input.value != input.nextSibling.value) { + var key = input.parentElement.previousSibling.firstChild.textContent; + if (input.value == "") { + input.value = input.nextSibling.value; + input.style.backgroundColor = "white"; + overridenTranslations.delete(key); + var value = input.nextSibling.value; + } else { + var value = input.value; + if (pageTranslations.get(key).rawText != escapeHTML(value)) { + input.style.backgroundColor = "#8BAB2E"; + overridenTranslations.set(key, value); + } else { + input.style.backgroundColor = "white"; + overridenTranslations.delete(key); + } + } + saveTranslations(); + if (isJSHasChanged(key)) { + location.reload(); + } + updateTranslationOnPage(key, value); + } + } + + function isJSHasChanged(key) { + var result = false; + if (pageTranslations.has(key)) { + var addresses = pageTranslations.get(key).addresses; + for (let i = 0; i < addresses.length; i++) { + var nodeName = addresses[i].node.nodeName; + if (nodeName == "SCRIPT") { + result = true; + } + } + } + return result; + } + + function updateTranslationOnPage(key, value) { + var propInfo = pageTranslations.get(key); + var addresses = propInfo.addresses; + for (let i = 0; i < addresses.length; i++) { + var node = addresses[i].node; + var number = addresses[i].number + 1; + var content = node.textContent; + var formattedValue = formatTranslation(value, addresses[i].args); + var regexStr = resultSep + "[^" + resultSepChars + "]*" + resultSep; + const regEx = new RegExp("^(?:[^" + resultSepChars + "]*" + regexStr + "){" + number + "}"); + var newString = content.replace(regEx, + function(x) { + return x.replace(RegExp(regexStr + "$"), resultSep + formattedValue + resultSep); + }); + node.textContent = newString; + } + } + + function formatTranslation(value, args) { + for (let i = 0; i < args.length; i++) { + value = value.replaceAll("{" + i + "}", args[i]); + } + return value; + } + + function parseHTMLTranslations() { + var translatedTexts = []; + var translatedAttrs = []; + var xpath = "//attribute::*[contains(., '" + startSep + "')]"; + var result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null); + var node = null; + var node = null; + while (node = result.iterateNext()) { + translatedAttrs.push(node); + parsePropsInNode(node); + } + xpath = "//*[text()[contains(.,'" + startSep + "')]]"; + result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null); + while (node = result.iterateNext()) { + translatedTexts.push(node); + parsePropsInNode(node); + } + readTranslations(); + removePropInfoFromPage(); + updatePageWithOverridenTranslations(); + reloadJS(); + } + + function reloadJS() { + var scriptBlocks = document.getElementsByTagName('script'); + for (let i = 0; i < scriptBlocks.length; i++) { + var scriptBlock = scriptBlocks[i]; + if (scriptBlock.hasAttribute("src")) { + var srcAttr = scriptBlock.getAttribute("src"); + if (!srcAttr.includes("translations.js")) { + if (srcAttr.indexOf("?") == -1) { + srcAttr += "?" + new Date().getTime(); + } else { + srcAttr += "&" + new Date().getTime(); + } + scriptBlock.remove(); + addJSLink(srcAttr); + } + } else { + var content = scriptBlock.textContent; + scriptBlock.remove(); + addInlineJS(content); + } + } + } + + function addInlineJS(content) { + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.textContent = content; + head.appendChild(script); + } + + function addJSLink(fileUrl) { + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.type = "text/javascript"; + script.src = fileUrl; + head.appendChild(script); + } + + function updatePageWithOverridenTranslations() { + for (let [key, value] of overridenTranslations) { + if (pageTranslations.has(key)) { + updateTranslationOnPage(key, value); + } + } + } + + function removePropInfoFromPage() { + for (let [key, propInfo] of pageTranslations) { + var addresses = propInfo.addresses; + for (let i = 0; i < addresses.length; i++) { + var node = addresses[i].node; + var content = node.textContent; + var regexStr = startSep + "[^" + endSep + "]*" + intSep + "([^" + endSep + intSep + "]*)" + endSep; + const regEx = new RegExp(regexStr, "g"); + var newString = content.replaceAll(regEx, resultSep + "$1" + resultSep); + node.textContent = newString; + } + } + } + + function parsePropsInNode(node) { + + if (node.nodeType === 1){ + var childs = node.childNodes; + childs.forEach(function(child){ + if (child.nodeType === 3){ + parsePropsInTextNode(child); + } + }); + }else if(node.nodeType === 2){ + parsePropsInTextNode(node); + } + + } + function parsePropsInTextNode(node){ + var i = 0; + var textString = node.textContent; + while (textString.indexOf(startSep) >= 0) { + textString = textString.substring(textString.indexOf(startSep) + startSep.length); + var prop = textString.substring(0, textString.indexOf(endSep)); + var address = new PropAddr(node, i, []); + addToPageTranslations(prop, address); + i++; + } + } + + function addToPageTranslations(prop, address) { + var key = prop.substring(0, prop.indexOf(intSep)); + prop = prop.substring(prop.indexOf(intSep) + intSep.length); + var rawText = prop.substring(0, prop.indexOf(intSep)); + prop = prop.substring(prop.indexOf(intSep) + intSep.length); + var textArgs = []; + while (prop.indexOf(intSep) >= 0) { + var textArg = prop.substring(0, prop.indexOf(intSep)); + prop = prop.substring(prop.indexOf(intSep) + intSep.length); + textArgs.push(textArg); + } + address.args = textArgs; + var formText = prop; + var propInfo = null; + if (pageTranslations.has(key)) { + propInfo = pageTranslations.get(key); + propInfo.addresses.push(address); + } else { + propInfo = new PropInfo(rawText, formText, address); + pageTranslations.set(key, propInfo); + } + } + function escapeHTML(input) { + return input + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/‘/g, "‘") + .replace(/’/g, "’"); + } + function unescapeHTML(input) { + return input + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, "\"") + .replace(/'/g, "'") + .replace(/‘/g, "‘") + .replace(/’/g, "’"); + } + +window.addEventListener('load', function() { + setTimeout(function() { + var developerSetting = document.getElementById("developer_i18n_onlineTranslation"); + if (developerSetting !== null && developerSetting.checked) { + parseHTMLTranslations(); + createTranslationPanel(); + } + }, 1000); +}) diff --git a/webapp/src/main/webapp/js/individual/manageLabelsForIndividual.js b/webapp/src/main/webapp/js/individual/manageLabelsForIndividual.js index 075a487a2..1510cc219 100644 --- a/webapp/src/main/webapp/js/individual/manageLabelsForIndividual.js +++ b/webapp/src/main/webapp/js/individual/manageLabelsForIndividual.js @@ -35,7 +35,6 @@ var manageLabels = { // Initial page setup. Called only at page load. initPage: function() { - var disableSubmit = true; if(this.submissionErrorsExist == "false") { //hide the form to add label this.addLabelForm.hide(); @@ -44,7 +43,11 @@ var manageLabels = { if(this.numberAvailableLocales == 0) { manageLabels.showFormButtonWrapper.hide(); this.showCancelOnlyButton.show(); - } else { + } else if(manageLabels.localeEntryExisting == "true") { + // if there is already an label for the selected langauge hide the add button + manageLabels.showFormButtonWrapper.hide(); + this.showCancelOnlyButton.show(); + } else{ //if the add label button is visible, don't need cancel only link this.showCancelOnlyButton.hide(); } @@ -53,19 +56,6 @@ var manageLabels = { //Display the form this.onShowAddForm(); - //Also make sure the save button is enabled in case there is a value selected for the drop down - if(this.labelLanguage.val() != "") { - disableSubmit = false; - } - - } - - - - if(disableSubmit) { - //disable submit until user selects a language - this.submit.attr('disabled', 'disabled'); - this.submit.addClass('disabledSubmit'); } this.bindEventListeners(); @@ -74,18 +64,6 @@ var manageLabels = { bindEventListeners: function() { - this.labelLanguage.change( function() { - //if language selected, allow submission, otherwise disallow - var selectedLanguage = manageLabels.labelLanguage.val(); - if(selectedLanguage != "") { - manageLabels.submit.attr('disabled', false); - manageLabels.submit.removeClass('disabledSubmit'); - } else { - manageLabels.submit.attr('disabled', 'disabled'); - manageLabels.submit.addClass('disabledSubmit'); - } - }); - //enable form to add label to be displayed or hidden this.showFormButton.click(function() { //clear the inputs for the label if the button is being clicked @@ -123,9 +101,6 @@ var manageLabels = { clearAddForm:function() { //clear inputs and select manageLabels.addLabelForm.find("input[type='text'],select").val(""); - //set the button for save to be disabled again - manageLabels.submit.attr('disabled', 'disabled'); - manageLabels.submit.addClass('disabledSubmit'); }, onShowAddForm:function() { manageLabels.addLabelForm.show(); @@ -226,12 +201,14 @@ var manageLabels = { var availableLocalesList = []; var listLen = selectLocalesFullList.length; var i; + var showAddButton = false; for(i = 0; i < listLen; i++) { var possibleLanguageInfo = selectLocalesFullList[i]; var possibleLanguageCode = possibleLanguageInfo["code"]; var possibleLangaugeLabel = possibleLanguageInfo["label"]; if(!(possibleLanguageCode in existingLanguages)) { //manageLabels.addLanguageCode(possibleLanguageCode, possibleLanguageLabel); + if (possibleLanguageCode == manageLabels.currentSelectedLocale) showAddButton = true; availableLocalesList.push(possibleLanguageInfo); } } @@ -242,8 +219,9 @@ var manageLabels = { var compB = b["label"]; return compA < compB ? -1 : 1; }); + //Re-show the add button and the form if they were hidden before - if(availableLocalesList.length > 0 && manageLabels.showFormButtonWrapper.is(":hidden")) { + if(availableLocalesList.length > 0 && manageLabels.showFormButtonWrapper.is(":hidden") && showAddButton) { manageLabels.showFormButtonWrapper.show(); //hide the cancel only button manageLabels.showCancelOnlyButton.hide(); diff --git a/webapp/src/main/webapp/js/menupage/browseByVClass.js b/webapp/src/main/webapp/js/menupage/browseByVClass.js index 4aec58fa6..940810bac 100644 --- a/webapp/src/main/webapp/js/menupage/browseByVClass.js +++ b/webapp/src/main/webapp/js/menupage/browseByVClass.js @@ -234,9 +234,9 @@ var browseByVClass = { var alpha = this.selectedAlpha(alpha); if ( alpha != "all" ) { - nothingToSeeHere = '

' + browseByVClass.thereAreNo + ' ' + vclass.name + ' ' + browseByVClass.indNamesStartWith + ' '+ alpha.toUpperCase() +'.

' + browseByVClass.tryAnotherLetter + '

'; + nothingToSeeHere = '

' + browseByVClass.thereAreNoEntriesStartingWith + ' '+ alpha.toUpperCase() +'.

' + browseByVClass.tryAnotherLetter + '

'; } else { - nothingToSeeHere = '

' + browseByVClass.thereAreNo + ' ' + vclass.name + ' ' + browseByVClass.indsInSystem + '

' + browseByVClass.selectAnotherClass + '

'; + nothingToSeeHere = '

' + browseByVClass.thereAreNoEntriesStartingWith + '

' + browseByVClass.selectAnotherClass + '

'; } browseByVClass.individualsContainer.prepend(nothingToSeeHere); diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl deleted file mode 100644 index 8831581f6..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl +++ /dev/null @@ -1,68 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that an account has been created. --> - -<#assign subject = "Your ${siteName} account has been created." /> - -<#assign html> - - - ${subject} - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- Congratulations! -

- -

- We have created your new account on ${siteName}, associated with ${userAccount.emailAddress}. -

- -

- If you did not request this new account you can safely ignore this email. - This request will expire if not acted upon for 30 days. -

- -

- Click the link below to create your password for your new account using our secure server. -

- -

- ${passwordLink} -

- -

- If the link above doesn't work, you can copy and paste the link directly into your browser's address bar. -

- -

- Thanks! -

- - - - -<#assign text> -${userAccount.firstName} ${userAccount.lastName} - -Congratulations! - -We have created your new account on ${siteName}, -associated with ${userAccount.emailAddress}. - -If you did not request this new account you can safely ignore this email. -This request will expire if not acted upon for 30 days. - -Paste the link below into your browser's address bar to create your password -for your new account using our secure server. - -${passwordLink} - -Thanks! - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedExternalOnlyEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedExternalOnlyEmail.ftl deleted file mode 100644 index b502008f4..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-acctCreatedExternalOnlyEmail.ftl +++ /dev/null @@ -1,43 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that an account has been created. --> - -<#assign subject = "Your ${siteName} account has been created." /> - -<#assign html> - - - ${subject} - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- Congratulations! -

- -

- We have created your new VIVO account associated with ${userAccount.emailAddress}. -

- -

- Thanks! -

- - - - -<#assign text> -${userAccount.firstName} ${userAccount.lastName} - -Congratulations! - -We have created your new VIVO account associated with -${userAccount.emailAddress}. - -Thanks! - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl deleted file mode 100644 index cfa872e9f..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl +++ /dev/null @@ -1,38 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that the user has changed his email account. --> - -<#assign subject = "Your ${siteName} email account has been changed." /> - -<#assign html> - - - ${subject} - - -

- Hi, ${userAccount.firstName} ${userAccount.lastName} -

- -

- You recently changed the email address associated with - ${userAccount.firstName} ${userAccount.lastName} -

- -

- Thank you. -

- - - - -<#assign text> -Hi, ${userAccount.firstName} ${userAccount.lastName} - -You recently changed the email address associated with -${userAccount.firstName} ${userAccount.lastName} - -Thank you. - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-createPassword.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-createPassword.ftl index a3ba4273c..126a2626f 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-createPassword.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-createPassword.ftl @@ -26,7 +26,7 @@
- + diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl deleted file mode 100644 index 1c43a27cc..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl +++ /dev/null @@ -1,43 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that an account has been created for an externally-authenticated user. --> - -<#assign subject = "Your ${siteName} account has been created." /> - -<#assign html> - - - ${subject} - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- Congratulations! -

- -

- We have created your new VIVO account associated with ${userAccount.emailAddress}. -

- -

- Thanks! -

- - - - -<#assign text> -${userAccount.firstName} ${userAccount.lastName} - -Congratulations! - -We have created your new VIVO account associated with -${userAccount.emailAddress}. - -Thanks! - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl index 7bd14ea42..c2ee386cf 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl @@ -56,7 +56,7 @@ var proxyContextInfo = { ajaxUrl: '${formUrls.proxyAjax}' }; var i18nStrings = { - selectEditorAndProfile: '${i18n().select_editor_and_profile}' + selectEditorAndProfile: '${i18n().select_editor_and_profile?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl deleted file mode 100644 index 1321ad26b..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl +++ /dev/null @@ -1,43 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that an password has been created. --> - -<#assign subject = "Your ${siteName} password has successfully been created." /> - -<#assign html> - - - ${subject} - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- Password successfully created. -

- -

- Your new password associated with ${userAccount.emailAddress} has been created. -

- -

- Thank you. -

- - - - -<#assign text> -${userAccount.firstName} ${userAccount.lastName} - -Password successfully created. - -Your new password associated with ${userAccount.emailAddress} -has been created. - -Thank you. - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetCompleteEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetCompleteEmail.ftl deleted file mode 100644 index 93562b1b9..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetCompleteEmail.ftl +++ /dev/null @@ -1,44 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation that a password has been reset. --> - -<#assign subject = "Your ${siteName} password changed." /> - -<#assign html> - - - ${subject} - - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- Password successfully changed. -

- -

- Your new password associated with ${userAccount.emailAddress} has been changed. -

- -

- Thank you. -

- - - - -<#assign text> -${userAccount.firstName} ${userAccount.lastName} - -Password successfully changed. - -Your new password associated with ${userAccount.emailAddress} -has been changed. - -Thank you. - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetPendingEmail.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetPendingEmail.ftl deleted file mode 100644 index dbae3c6fc..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-passwordResetPendingEmail.ftl +++ /dev/null @@ -1,61 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#-- Confirmation email for user account password reset --> - -<#assign subject = "${siteName} reset password request" /> - -<#assign html> - - - ${subject} - - -

- Dear ${userAccount.firstName} ${userAccount.lastName}: -

- -

- We have received a request to reset the password for your ${siteName} account (${userAccount.emailAddress}). -

- -

- Please follow the instructions below to proceed with your password reset. -

- -

- If you did not request this new account you can safely ignore this email. - This request will expire if not acted upon within 30 days. -

- -

- Click on the link below or paste it into your browser's address bar to reset your password - using our secure server. -

- -

${passwordLink}

- -

Thank you!

- - - - -<#assign text> -Dear ${userAccount.firstName} ${userAccount.lastName}: - -We have received a request to reset the password for your ${siteName} account -(${userAccount.emailAddress}). - -Please follow the instructions below to proceed with your password reset. - -If you did not request this new account you can safely ignore this email. -This request will expire if not acted upon within 30 days. - -Paste the link below into your browser's address bar to reset your password -using our secure server. - -${passwordLink} - -Thank you! - - -<@email subject=subject html=html text=text /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl index cf11f0a72..645908854 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl @@ -26,7 +26,7 @@
- + diff --git a/webapp/src/main/webapp/templates/freemarker/body/admin/admin-sparqlQueryForm.ftl b/webapp/src/main/webapp/templates/freemarker/body/admin/admin-sparqlQueryForm.ftl index dc356e970..8133c91b0 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/admin/admin-sparqlQueryForm.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/admin/admin-sparqlQueryForm.ftl @@ -3,14 +3,14 @@ <#-- Template that presents the SPARQL query form. -->
-

SPARQL Query

+

${i18n().sparql_query_title}

-

Query:

+

${i18n().sparql_query_header}:

-

Format for SELECT and ASK query results:

+

${i18n().sparql_query_select_ask_results}:

@@ -19,7 +19,7 @@
-

Format for CONSTRUCT and DESCRIBE query results:

+

${i18n().sparql_query_construct_describe_results}:

@@ -29,10 +29,10 @@
-
+
- +
diff --git a/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl b/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl index 303311e14..55c0fb688 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl @@ -43,8 +43,8 @@
diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl index 2b44602b5..a4d589553 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl @@ -54,8 +54,8 @@ positionPredicate: '${positionPredicate}' }; var i18nStrings = { - dragDropMenus: '${i18n().drag_drop_to_reorder_menus}', - reorderingFailed: '${i18n().reordering_menus_failed}' + dragDropMenus: '${i18n().drag_drop_to_reorder_menus?js_string}', + reorderingFailed: '${i18n().reordering_menus_failed?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl index efbaedd82..2817b59d3 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl @@ -87,14 +87,14 @@ @@ -110,5 +110,5 @@ ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl index 199b3aa29..24a4effa9 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl @@ -31,6 +31,14 @@ <#else>

${i18n().manage_labels_capitalized}

+<#assign localeEntryExisting = true /> +<#if editConfiguration.pageData.localeEntryExisting?has_content> + <#assign localeEntryExisting = editConfiguration.pageData.localeEntryExisting /> + +<#assign currentSelectedLocale =" " /> +<#if editConfiguration.pageData.currentSelectedLocale?has_content> + <#assign currentSelectedLocale = editConfiguration.pageData.currentSelectedLocale /> + @@ -94,11 +102,13 @@ var customFormData = { individualUri: '${subjectUri!}', submissionErrorsExist: '${submissionErrorsExist}', selectLocalesFullList: selectLocalesFullList, - numberAvailableLocales:${availableLocalesNumber} + numberAvailableLocales:${availableLocalesNumber}, + localeEntryExisting : '${localeEntryExisting?c}', + currentSelectedLocale: '${currentSelectedLocale}' }; var i18nStrings = { - errorProcessingLabels: '${i18n().error_processing_labels}', - selectLocaleOptionString : '${i18n().select_locale}' + errorProcessingLabels: '${i18n().error_processing_labels?js_string}', + selectLocaleOptionString : '${i18n().select_locale?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualAddForm.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualAddForm.ftl index a60e30d86..57b23a552 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualAddForm.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualAddForm.ftl @@ -6,19 +6,6 @@

- - diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualMacros.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualMacros.ftl index 117b4b918..3eef638c1 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualMacros.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividualMacros.ftl @@ -81,7 +81,7 @@ <#macro displayLabel labelSeq labelStr languageCode labelEditLink tagOrTypeStr editGenerator editable displayRemoveLink>
  • ${labelStr} <#if labelSeq?seq_contains(labelStr)> (duplicate value) - <#if editable> <#if labelEditLink?has_content> Edit + <#if editable> <#if labelEditLink?has_content> ${i18n().edit_capitalized} <#if displayRemoveLink> ${i18n().remove_capitalized} diff --git a/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl b/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl index d0c1bd307..cf192af8b 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl @@ -189,7 +189,7 @@ var proxyContextInfo = { ajaxUrl: '${formUrls.ajax}' }; var i18nStrings = { - selectEditorAndProfile: '${i18n().select_editor_and_profile}' + selectEditorAndProfile: '${i18n().select_editor_and_profile?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl b/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl index af0636a8a..e00b4c448 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl @@ -75,7 +75,7 @@ ${stylesheets.add('', diff --git a/webapp/src/main/webapp/templates/freemarker/body/partials/browse-classgroups.ftl b/webapp/src/main/webapp/templates/freemarker/body/partials/browse-classgroups.ftl index 28fb6e4ce..c3ed982b7 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/partials/browse-classgroups.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/partials/browse-classgroups.ftl @@ -72,8 +72,8 @@ ${stylesheets.add(' <#if tabCount = 1 > -
  • ${groupName?capitalize}
  • +
  • ${p.capitalizeGroupName(groupName)}
  •  
  • <#assign tabCount = 2> <#else> -
  • ${groupName?capitalize}
  • +
  • ${p.capitalizeGroupName(groupName)}
  •  
  • @@ -50,7 +50,7 @@ <#if groupName?has_content> <#--the function replaces spaces in the name with underscores, also called for the property group menu--> <#assign groupNameHtmlId = p.createPropertyGroupHtmlId(groupName) > - + <#else> diff --git a/webapp/src/main/webapp/templates/freemarker/body/partials/menupage/menupage-scripts.ftl b/webapp/src/main/webapp/templates/freemarker/body/partials/menupage/menupage-scripts.ftl index 3459054c4..9ad5892b6 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/partials/menupage/menupage-scripts.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/partials/menupage/menupage-scripts.ftl @@ -30,14 +30,13 @@ defaultBrowseVClassUri: firstBrowseClass //'${firstNonEmptyVClass}' }; var i18nStrings = { - pageString: '${i18n().page}', - viewPageString: '${i18n().view_page}', - ofTheResults: '${i18n().of_the_results}', - thereAreNo: '${i18n().there_are_no}', - indNamesStartWith: '${i18n().individuals_names_starting_with}', - tryAnotherLetter: '${i18n().try_another_letter}', - indsInSystem: '${i18n().individuals_in_system}', - selectAnotherClass: '${i18n().select_another_class}' + pageString: '${i18n().page?js_string}', + viewPageString: '${i18n().view_page?js_string}', + ofTheResults: '${i18n().of_the_results?js_string}', + thereAreNoEntriesStartingWith: '${i18n().there_are_no_entries_starting_with?js_string}', + tryAnotherLetter: '${i18n().try_another_letter?js_string}', + indsInSystem: '${i18n().individuals_in_system?js_string}', + selectAnotherClass: '${i18n().select_another_class?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/search/search-help.ftl b/webapp/src/main/webapp/templates/freemarker/body/search/search-help.ftl deleted file mode 100644 index b69e4a57f..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/search/search-help.ftl +++ /dev/null @@ -1,43 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -<#if origination?has_content && origination == "helpLink"> -

    Search Tips

    - - Back to results - -<#else> -

    Search Tips

    - -
      -
    • Keep it simple! Use short, single terms unless your searches are returning too many results.
    • -
    • Use quotes to search for an entire phrase -- e.g., "protein folding".
    • -
    • Except for boolean operators, searches are not case-sensitive, so "Geneva" and "geneva" are equivalent
    • -
    • If you are unsure of the correct spelling, put ~ at the end of your search term -- e.g., cabage~ finds cabbage, steven~ finds Stephen and Stefan (as well as other similar names).
    • -
    - -

    Advanced Tips

    - - -${stylesheets.add('')} - diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl index 3cb450f96..6b48e53c9 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl @@ -39,15 +39,15 @@ diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl index fac6c3b9e..9244c1403 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl @@ -47,19 +47,19 @@ var json = [${jsonTree!}]; var propertyType = '${propType}'; var i18nStrings = { - hideProperties: '${i18n().hide_properties}', - showProperties: '${i18n().show_properties}', - localNameString: '${i18n().local_name}', - groupString: '${i18n().group_capitalized}', - domainClass: '${i18n().domain_class}', - rangeClass: '${i18n().range_class}', - rangeDataType: '${i18n().range_data_type}', - expandAll: '${i18n().expand_all}', - collapseAll: '${i18n().collapse_all}', - subProperties: '${i18n().sub_properties}', - displayRank: '${i18n().display_rank}', - subProperty: '${i18n().subproperty}', - propertiesString: '${i18n().properties_capitalized}' + hideProperties: '${i18n().hide_properties?js_string}', + showProperties: '${i18n().show_properties?js_string}', + localNameString: '${i18n().local_name?js_string}', + groupString: '${i18n().group_capitalized?js_string}', + domainClass: '${i18n().domain_class?js_string}', + rangeClass: '${i18n().range_class?js_string}', + rangeDataType: '${i18n().range_data_type?js_string}', + expandAll: '${i18n().expand_all?js_string}', + collapseAll: '${i18n().collapse_all?js_string}', + subProperties: '${i18n().sub_properties?js_string}', + displayRank: '${i18n().display_rank?js_string}', + subProperty: '${i18n().subproperty?js_string}', + propertiesString: '${i18n().properties_capitalized?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/body/termsOfUse.ftl b/webapp/src/main/webapp/templates/freemarker/body/termsOfUse.ftl deleted file mode 100644 index 205a0012e..000000000 --- a/webapp/src/main/webapp/templates/freemarker/body/termsOfUse.ftl +++ /dev/null @@ -1,34 +0,0 @@ -<#-- $This file is distributed under the terms of the license in LICENSE$ --> - -
    -

    Terms of Use

    - -

    Disclaimers

    -

    This ${termsOfUse.siteName} website contains material—text information, publication - citations, links, and images—provided by ${termsOfUse.siteHost} and by various - third parties, both individuals and organizations, commercial and otherwise. To the extent copyrightable, - the information presented on the VIVO website and available as Resource Description Framework (RDF) data - from VIVO at ${termsOfUse.siteHost} is intended for public use and is freely distributed under the terms of the - Creative Commons CC-BY 3.0 license which allows you - to copy, distribute, display and make derivatives of this information provided you give credit to - ${termsOfUse.siteHost}. Any non-copyrightable information is available to you under a - CC0 waiver. However, source documents, - images or web pages attached to or linked from VIVO may contain copyrighted information and should only be - used or distributed under terms included with each source or in accordance with the principles of fair use. -

    - -

    Disclaimer of Liability

    -

    ${termsOfUse.siteHost?cap_first} makes no warranty, expressed or implied, including the warranties of merchantability - and fitness for a particular purpose, or assumes any legal liability or responsibility for the accuracy, - completeness, currency or usefulness of any material displayed or distributed through the - ${termsOfUse.siteName} website or represents that its use would not infringe privately owned rights. - ${termsOfUse.siteHost?cap_first} disclaims all warranties with regard to the information provided. Any reliance upon such information - is at your own risk. In no event will ${termsOfUse.siteHost} be liable to you for any damages or losses whatsoever resulting - from or caused by the ${siteName} website or its contents.

    - -

    Disclaimer of Endorsement

    -

    Reference herein to any specific commercial product, process, or service by trade name, - trademark, manufacturer, or otherwise, does not necessarily constitute or imply its endorsement or recommendation - by ${termsOfUse.siteHost}. The views and opinions of authors expressed herein do not necessarily state or reflect those of - ${termsOfUse.siteHost} and shall not be used for advertising or product endorsement purposes.

    -
    diff --git a/webapp/src/main/webapp/templates/freemarker/edit/dateTimeWithPrecision.ftl b/webapp/src/main/webapp/templates/freemarker/edit/dateTimeWithPrecision.ftl index 5f050e0b9..cc00ed613 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/dateTimeWithPrecision.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/dateTimeWithPrecision.ftl @@ -78,9 +78,9 @@ precisionConstants.second -- URI for precision <#if precLevel gte 2> - + required > - + <#assign numDays = 31 /> <#list 1..numDays as currentDay> @@ -101,9 +101,9 @@ precisionConstants.second -- URI for precision <#if precLevel gte 4> <#-- We'll need to make this more flexible to support 24 hour display down the road. For now assuming 12h with am/pm --> - + required > - + <#assign numMinutes = 59 /> <#list 1..numMinutes as currentMinute> @@ -135,9 +135,9 @@ precisionConstants.second -- URI for precision <#if precLevel gte 6> - + - or + ${i18n().or} ${i18n().cancel_link}

    @@ -124,9 +124,9 @@ Also multiple types parameter set to true only if more than one type returned--> baseHref: '${urls.base}/individual?uri=' }; var i18nStrings = { - selectAnExisting: '${i18n().select_an_existing}', - orCreateNewOne: '${i18n().or_create_new_one}', - selectedString: '${i18n().selected}' + selectAnExisting: '${i18n().select_an_existing?js_string}', + orCreateNewOne: '${i18n().or_create_new_one?js_string}', + selectedString: '${i18n().selected?js_string}' }; <#-- diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/dateTimeEntryForm.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/dateTimeEntryForm.ftl index 45b992577..b80f63688 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/dateTimeEntryForm.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/dateTimeEntryForm.ftl @@ -3,13 +3,13 @@
    <#if datatype?contains("#date") || datatype?contains("Year") > - + <#if datatype?contains("#date") || datatype?contains("Month") > - + <#if datatype?contains("#date") > - + <#if datatype?contains("#dateTime") || datatype?contains("#time") > - + - + - + - or + ${i18n().or} ${i18n().cancel_link}

    diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/defaultDataPropertyForm.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/defaultDataPropertyForm.ftl index 62ab74c31..1e3750385 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/defaultDataPropertyForm.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/defaultDataPropertyForm.ftl @@ -72,7 +72,7 @@ the default label for default data/object property editing is returned from Edit Configuration Template Model, but that method may not return the correct result for other custom forms--> - or + ${i18n().or} ${i18n().cancel_link} @@ -85,14 +85,14 @@ var datatype = "${datatype!}"; var i18nStrings = { - four_digit_year: '${i18n().four_digit_year}', - year_numeric: '${i18n().year_numeric}', - year_month_day: '${i18n().year_month_day}', - minimum_ymd: '${i18n().minimum_ymd}', - minimum_hour: '${i18n().minimum_hour}', - year_month: '${i18n().year_month}', - decimal_only: '${i18n().decimal_only}', - whole_number: '${i18n().whole_number}' + four_digit_year: '${i18n().four_digit_year?js_string}', + year_numeric: '${i18n().year_numeric?js_string}', + year_month_day: '${i18n().year_month_day?js_string}', + minimum_ymd: '${i18n().minimum_ymd?js_string}', + minimum_hour: '${i18n().minimum_hour?js_string}', + year_month: '${i18n().year_month?js_string}', + decimal_only: '${i18n().decimal_only?js_string}', + whole_number: '${i18n().whole_number?js_string}' }; diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/menuManagement.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/menuManagement.ftl index 4643480ed..54b5b0296 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/menuManagement.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/menuManagement.ftl @@ -99,13 +99,13 @@ ${stylesheets.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--browseClassGroups.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--browseClassGroups.ftl index d912e1641..9f4688aed 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--browseClassGroups.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--browseClassGroups.ftl @@ -59,10 +59,10 @@ <#--Include JavaScript specific to the types of data getters related to this content--> diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--fixedHtml.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--fixedHtml.ftl index d7252b2eb..727c76a35 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--fixedHtml.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--fixedHtml.ftl @@ -13,11 +13,11 @@ ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--searchIndividuals.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--searchIndividuals.ftl index 737bbbf89..1a9a55dce 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--searchIndividuals.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--searchIndividuals.ftl @@ -22,12 +22,12 @@ ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--sparqlQuery.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--sparqlQuery.ftl index 2a1bc7db5..e04796764 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--sparqlQuery.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement--sparqlQuery.ftl @@ -15,11 +15,11 @@ ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl index 64b7473da..550ae8b39 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl @@ -176,21 +176,21 @@ <#include "pageManagement--customDataScript.ftl"> diff --git a/webapp/src/main/webapp/templates/freemarker/lib/lib-properties.ftl b/webapp/src/main/webapp/templates/freemarker/lib/lib-properties.ftl index 3e5223fc6..298a63132 100644 --- a/webapp/src/main/webapp/templates/freemarker/lib/lib-properties.ftl +++ b/webapp/src/main/webapp/templates/freemarker/lib/lib-properties.ftl @@ -183,16 +183,16 @@ name will be used as the label. --> <#local url = statement.editUrl> <#if url?has_content> <#if propertyLocalName?contains("ARG_2000028")> - <#if rangeUri?contains("Address")> - <#local url = url + "&addressUri=" + "${statement.address!}"> - <#elseif rangeUri?contains("Telephone") || rangeUri?contains("Fax")> - <#local url = url + "&phoneUri=" + "${statement.phone!}"> - <#elseif rangeUri?contains("Work") || rangeUri?contains("Email")> - <#local url = url + "&emailUri=" + "${statement.email!}"> - <#elseif rangeUri?contains("Name")> - <#local url = url + "&fullNameUri=" + "${statement.fullName!}"> - <#elseif rangeUri?contains("Title")> - <#local url = url + "&titleUri=" + "${statement.title!}"> + <#if rangeUri?contains("Address") && statement.address??> + <#local url = url + "&addressUri=" + "${statement.address?url}"> + <#elseif (rangeUri?contains("Telephone") || rangeUri?contains("Fax")) && statement.phone??> + <#local url = url + "&phoneUri=" + "${statement.phone?url}"> + <#elseif (rangeUri?contains("Work") || rangeUri?contains("Email")) && statement.email??> + <#local url = url + "&emailUri=" + "${statement.email?url}"> + <#elseif rangeUri?contains("Name") && statement.fullName??> + <#local url = url + "&fullNameUri=" + "${statement.fullName?url}"> + <#elseif rangeUri?contains("Title") && statement.title??> + <#local url = url + "&titleUri=" + "${statement.title?url}"> <@showEditLink propertyLocalName rangeUri url /> @@ -342,3 +342,6 @@ name will be used as the label. --> <#return groupName> +<#function capitalizeGroupName propertyGroupName> + <#return propertyGroupName?capitalize> + diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl index 359935a28..70489d4fd 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl @@ -7,3 +7,5 @@ ${scripts.add('')} ${scripts.add('')} +${scripts.add('')} +${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/developerPanel.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/developerPanel.ftl index 4ab08a563..06ad70855 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/developerPanel.ftl @@ -19,6 +19,9 @@ <#elseif !settings.mayControl>

    ${siteName} is running in developer mode.

    +
    <#else>
    @@ -56,6 +59,8 @@ "Defeat the cache of language property files" /> <@showCheckbox "developer_i18n_logStringRequests", "Log the retrieval of language strings" /> + <@showCheckbox "developer_i18n_onlineTranslation", + "Enable online translation" />
    diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/doctype.html b/webapp/src/main/webapp/templates/freemarker/page/partials/doctype.html index 54a77eca4..6c12f1115 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/doctype.html +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/doctype.html @@ -1,3 +1,3 @@ - + diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl index bc71431c8..4cb139f9d 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl @@ -3,7 +3,7 @@ <#-- Template for scripts that must be loaded in the head --> diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/languageSelector.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/languageSelector.ftl index 80a0c633f..9bb4b9047 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/languageSelector.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/languageSelector.ftl @@ -1,18 +1,12 @@ <#-- $This file is distributed under the terms of the license in LICENSE$ --> -<#-- - How can this done with images instead of buttons containing images? - Why don't the "alt" values show as tooltips?" - What was the right way to do this? - --> - <#-- This is included by identity.ftl --> <#if selectLocale??> -
    • ${i18n().select_a_language}