From 9f19f2c38d73f75f96b65e1a43e44ec41821cacd Mon Sep 17 00:00:00 2001 From: j2blake Date: Tue, 10 Dec 2013 16:39:03 -0500 Subject: [PATCH 01/31] Add a refinement to the developer panel --- webapp/web/js/developer/developerPanel.js | 2 ++ .../web/templates/freemarker/page/partials/developerPanel.ftl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index f7d881709..201168ba9 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -40,6 +40,7 @@ function DeveloperPanel(developerAjaxUrl) { function updateDisabledFields() { var developerEnabled = document.getElementById("developerEnabled").checked; + document.getElementById("developerPermitAnonymousControl").disabled = !developerEnabled; document.getElementById("developerDefeatFreemarkerCache").disabled = !developerEnabled; document.getElementById("developerInsertFreemarkerDelimiters").disabled = !developerEnabled; document.getElementById("developerPageContentsLogCustomListView").disabled = !developerEnabled; @@ -57,6 +58,7 @@ function DeveloperPanel(developerAjaxUrl) { function collectFormData() { var data = new Object(); getCheckbox("developerEnabled", data); + getCheckbox("developerPermitAnonymousControl", data); getCheckbox("developerDefeatFreemarkerCache", data); getCheckbox("developerInsertFreemarkerDelimiters", data); getCheckbox("developerPageContentsLogCustomListView", data); diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index a9437a3d8..9b8b3ca3f 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -25,6 +25,10 @@ <@showCheckbox "developerEnabled" /> Enable developer mode +
From 0b03133a32ce2aade0426e1f69c2e2e2b4b2b89a Mon Sep 17 00:00:00 2001 From: tworrall Date: Wed, 18 Dec 2013 11:48:27 -0500 Subject: [PATCH 02/31] minor css change for the search page facet --- webapp/web/css/vitro.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp/web/css/vitro.css b/webapp/web/css/vitro.css index 464f0480a..b90e88002 100644 --- a/webapp/web/css/vitro.css +++ b/webapp/web/css/vitro.css @@ -120,11 +120,13 @@ text-align: left; } .searchTOC ul li { - display: block; border-bottom: 1px solid #dde4e3; + display: block; font-size: 14px; + margin-left: 20px; padding-top: .5em; padding-bottom: .5em; + width: 150px; } .searchTOC ul li:hover { background-color:#f7f9f9; @@ -134,7 +136,6 @@ } .searchTOC ul a { display: inline; - padding-left: 25px; } .searchTOC ul span { From 2e8ef62bb178e8721a3495f3fc62c6a49eab156f Mon Sep 17 00:00:00 2001 From: j2blake Date: Thu, 19 Dec 2013 11:44:10 -0500 Subject: [PATCH 03/31] Line-ending characters --- .../testrunner/CommandRunnerException.java | 52 +- .../utilities/testrunner/FatalException.java | 52 +- .../testrunner/SeleniumRunnerParameters.java | 868 +++++++++--------- .../testrunner/datamodel/DataModel.java | 588 ++++++------ .../output/OutputSummaryFormatter.java | 750 +++++++-------- 5 files changed, 1155 insertions(+), 1155 deletions(-) diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/CommandRunnerException.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/CommandRunnerException.java index 8550abbb7..31074b2e7 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/CommandRunnerException.java +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/CommandRunnerException.java @@ -1,26 +1,26 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.utilities.testrunner; - -/** - * Indicates a problem with the attempt to run a command in a sub-process. - */ -public class CommandRunnerException extends Exception { - - public CommandRunnerException() { - super(); - } - - public CommandRunnerException(String message) { - super(message); - } - - public CommandRunnerException(Throwable cause) { - super(cause); - } - - public CommandRunnerException(String message, Throwable cause) { - super(message, cause); - } - -} +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.utilities.testrunner; + +/** + * Indicates a problem with the attempt to run a command in a sub-process. + */ +public class CommandRunnerException extends Exception { + + public CommandRunnerException() { + super(); + } + + public CommandRunnerException(String message) { + super(message); + } + + public CommandRunnerException(Throwable cause) { + super(cause); + } + + public CommandRunnerException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FatalException.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FatalException.java index 42b11ef7b..98721bed1 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FatalException.java +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FatalException.java @@ -1,26 +1,26 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.utilities.testrunner; - -/** - * Indicates a problem so severe that we might as well stop now. - */ -public class FatalException extends RuntimeException { - - public FatalException() { - super(); - } - - public FatalException(String message) { - super(message); - } - - public FatalException(Throwable cause) { - super(cause); - } - - public FatalException(String message, Throwable cause) { - super(message, cause); - } - -} +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.utilities.testrunner; + +/** + * Indicates a problem so severe that we might as well stop now. + */ +public class FatalException extends RuntimeException { + + public FatalException() { + super(); + } + + public FatalException(String message) { + super(message); + } + + public FatalException(Throwable cause) { + super(cause); + } + + public FatalException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/SeleniumRunnerParameters.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/SeleniumRunnerParameters.java index 1fd8020f8..bb04f3ed4 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/SeleniumRunnerParameters.java +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/SeleniumRunnerParameters.java @@ -1,434 +1,434 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.utilities.testrunner; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Properties; - -import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener; -import edu.cornell.mannlib.vitro.utilities.testrunner.listener.LoggingListener; -import edu.cornell.mannlib.vitro.utilities.testrunner.listener.MulticastListener; - -/** - * Holds the runtime parameters that are read from the properties file, perhaps - * with modifications from the GUI if we are running interactively. - */ -public class SeleniumRunnerParameters { - public static final String PROP_OUTPUT_DIRECTORY = "output_directory"; - public static final String PROP_UPLOAD_DIRECTORY = "upload_directory"; - public static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories"; - public static final String PROP_WEBSITE_URL = "website_url"; - public static final String PROP_USER_EXTENSIONS_PATH = "user_extensions_path"; - public static final String PROP_FIREFOX_PROFILE_PATH = "firefox_profile_template_path"; - public static final String PROP_SUITE_TIMEOUT_LIMIT = "suite_timeout_limit"; - public static final String PROP_SELENIUM_JAR_PATH = "selenium_jar_path"; - public static final String PROP_IGNORED_TESTS = "ignored_tests_file"; - public static final String PROP_SUMMARY_CSS = "summary_css_file"; - - public static final String LOGFILE_NAME = "log_file.txt"; - - private final String websiteUrl; - private final File userExtensionsFile; - private final File firefoxProfileDir; - private final int suiteTimeoutLimit; - private final File seleniumJarPath; - private final File uploadDirectory; - private final File outputDirectory; - private final File logFile; - private final Collection suiteParentDirectories; - private final ModelCleanerProperties modelCleanerProperties; - private final IgnoredTests ignoredTests; - - private boolean cleanModel = true; - private boolean cleanUploads = true; - - // If we fail during the parameter parsing, we'll still write the log - // somewhere. - private Listener listener = new LoggingListener(System.out); - - /** - * Read the required properties from the property file, and do some checks - * on them. - */ - public SeleniumRunnerParameters(String propertiesFilepath) - throws IOException { - Properties props = loadPropertiesFile(propertiesFilepath); - - this.websiteUrl = getRequiredProperty(props, PROP_WEBSITE_URL); - this.userExtensionsFile = checkReadableFile(props, - PROP_USER_EXTENSIONS_PATH); - this.firefoxProfileDir = checkOptionalReadableDirectory(props, - PROP_FIREFOX_PROFILE_PATH); - this.suiteTimeoutLimit = getRequiredIntegerProperty(props, - PROP_SUITE_TIMEOUT_LIMIT); - this.seleniumJarPath = checkReadableFile(props, PROP_SELENIUM_JAR_PATH); - this.uploadDirectory = checkReadWriteDirectory(props, - PROP_UPLOAD_DIRECTORY); - - this.outputDirectory = checkOutputDirectory(props); - this.logFile = new File(this.outputDirectory, LOGFILE_NAME); - this.listener = new MulticastListener(); - addListener(new LoggingListener(this.logFile)); - - this.suiteParentDirectories = checkSuiteParentDirectories(props); - - this.modelCleanerProperties = new ModelCleanerProperties(props); - - // Get the list of ignored tests. - String ignoredFilesPath = getRequiredProperty(props, PROP_IGNORED_TESTS); - File ignoredFilesFile = new File(ignoredFilesPath); - FileHelper.checkReadableFile(ignoredFilesFile, "File '" - + ignoredFilesPath + "'"); - this.ignoredTests = new IgnoredTests(ignoredFilesFile); - } - - /** - * Load the properties from the properties file. - */ - private Properties loadPropertiesFile(String propertiesFilepath) - throws FileNotFoundException, IOException { - File propsFile = new File(propertiesFilepath); - if (!propsFile.exists()) { - throw new FileNotFoundException("Property file does not exist: '" - + propsFile + "'"); - } - - Reader propsReader = null; - try { - propsReader = new FileReader(propsFile); - Properties props = new Properties(); - props.load(propsReader); - return props; - } finally { - if (propsReader != null) { - try { - propsReader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - /** - * If there is a parameter for this key, it should point to a readable - * directory. - */ - private File checkOptionalReadableDirectory(Properties props, String key) { - String value = props.getProperty(key); - if (value == null) { - return null; - } - - value = value.trim(); - if (value.trim().length() == 0) { - return null; - } - - File dir = new File(value); - - if (!dir.exists()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' does not exist."); - } - - if (!dir.isDirectory()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' is not a directory."); - } - - if (!dir.canRead()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' is not readable."); - } - - return dir; - } - - /** - * Check that there is a property for the required directory path, and that - * it points to a valid directory. - */ - private File checkReadWriteDirectory(Properties props, String key) { - String value = getRequiredProperty(props, key); - - File dir = new File(value); - - if (!dir.exists()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' does not exist. (" + dir.getAbsolutePath() - + ")"); - } - - if (!dir.isDirectory()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' is not a directory. (" + dir.getAbsolutePath() - + ")"); - } - - if (!dir.canRead()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' is not readable. (" + dir.getAbsolutePath() - + ")"); - } - - if (!dir.canWrite()) { - throw new IllegalArgumentException("Directory " + key + " '" - + value + "' is not writeable. (" + dir.getAbsolutePath() - + ")"); - } - return dir; - } - - private File checkReadableFile(Properties props, String key) { - String value = getRequiredProperty(props, key); - - File file = new File(value); - - if (!file.exists()) { - throw new IllegalArgumentException("File " + key + ": '" + value - + "' does not exist. (" + file.getAbsolutePath() + ")"); - } - if (!file.isFile()) { - throw new IllegalArgumentException("File " + key + ": '" + value - + "' is not a file. (" + file.getAbsolutePath() + ")"); - } - if (!file.canRead()) { - throw new IllegalArgumentException("File " + key + ": '" + value - + "' is not readable. (" + file.getAbsolutePath() + ")"); - } - return file; - } - - /** - * Get the property for the output directory. If it does not exist, create - * it (the parent must exist). Ensure that it is writeable. - */ - private File checkOutputDirectory(Properties props) throws IOException { - String value = getRequiredProperty(props, PROP_OUTPUT_DIRECTORY); - File outputDirectory = new File(value); - File outputParent = outputDirectory.getParentFile(); - - if (!outputDirectory.exists()) { - if (!outputParent.exists()) { - throw new IllegalArgumentException( - "Output directory does not exist, nor does its parent. '" - + outputDirectory + "' (" - + outputDirectory.getAbsolutePath() + ")"); - } - outputDirectory.mkdir(); - if (!outputDirectory.exists()) { - throw new IOException("Failed to create output directory: '" - + outputDirectory + "' (" - + outputDirectory.getAbsolutePath() + ")"); - } - } - - if (!outputDirectory.isDirectory()) { - throw new IllegalArgumentException("Suite directory '" - + outputDirectory.getPath() + "' is not a directory. (" - + outputDirectory.getAbsolutePath() + ")"); - } - if (!outputDirectory.canRead()) { - throw new IllegalArgumentException("Suite directory '" - + outputDirectory.getPath() + "' is not readable. (" - + outputDirectory.getAbsolutePath() + ")"); - } - if (!outputDirectory.canWrite()) { - throw new IllegalArgumentException("Suite directory '" - + outputDirectory.getPath() + "' is not writeable. (" - + outputDirectory.getAbsolutePath() + ")"); - } - - return outputDirectory; - } - - /** - * Get the property for the suite directories and ensure that each one is - * indeed a readable directory. - */ - private Collection checkSuiteParentDirectories(Properties props) { - String value = getRequiredProperty(props, PROP_SUITE_DIRECTORIES); - - List dirs = new ArrayList(); - String[] paths = value.split("[:;]"); - for (String path : paths) { - File dir = new File(path.trim()); - - if (!dir.exists()) { - throw new IllegalArgumentException("Suite directory '" - + dir.getPath() + "' does not exist. (" - + dir.getAbsolutePath() + ")"); - } - if (!dir.isDirectory()) { - throw new IllegalArgumentException("Suite directory '" - + dir.getPath() + "' is not a directory. (" - + dir.getAbsolutePath() + ")"); - } - if (!dir.canRead()) { - throw new IllegalArgumentException("Suite directory '" - + dir.getPath() + "' is not readable. (" - + dir.getAbsolutePath() + ")"); - } - dirs.add(dir); - } - return dirs; - } - - /** - * Get the value for this property. If there isn't one, or if it's empty, - * complain. - */ - private String getRequiredProperty(Properties props, String key) { - String value = props.getProperty(key); - if ((value == null) || (value.trim().length() == 0)) { - throw new IllegalArgumentException( - "Property file must provide a value for '" + key + "'"); - } - return value; - } - - /** - * This required property must be a valid integer. - */ - private int getRequiredIntegerProperty(Properties props, String key) { - String value = getRequiredProperty(props, key); - try { - return Integer.parseInt(value.trim()); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Property value for '" + key - + "' is not a valid integer: " + value); - } - } - - public String getWebsiteUrl() { - return websiteUrl; - } - - public File getUserExtensionsFile() { - return userExtensionsFile; - } - - public boolean hasFirefoxProfileDir() { - return firefoxProfileDir != null; - } - - public File getFirefoxProfileDir() { - return firefoxProfileDir; - } - - public int getSuiteTimeoutLimit() { - return suiteTimeoutLimit; - } - - public File getSeleniumJarPath() { - return seleniumJarPath; - } - - public void addListener(Listener l) { - if (listener instanceof MulticastListener) { - ((MulticastListener) listener).addListener(l); - } else { - throw new IllegalStateException("Listener is not a multi-cast -- " - + "can't add new listeners."); - } - } - - public Listener getListener() { - return listener; - } - - public void setListener(Listener logger) { - this.listener = logger; - } - - public File getUploadDirectory() { - return uploadDirectory; - } - - public File getOutputDirectory() { - return outputDirectory; - } - - public File getLogFile() { - return logFile; - } - - public Collection getSuiteParentDirectories() { - return suiteParentDirectories; - } - - public ModelCleanerProperties getModelCleanerProperties() { - return modelCleanerProperties; - } - - public IgnoredTests getIgnoredTests() { - return ignoredTests; - } - - public boolean isCleanModel() { - return cleanModel; - } - - public void setCleanModel(boolean cleanModel) { - this.cleanModel = cleanModel; - } - - public boolean isCleanUploads() { - return cleanUploads; - } - - public void setCleanUploads(boolean cleanUploads) { - this.cleanUploads = cleanUploads; - } - - public String toString() { - return "Parameters:" + "\n websiteUrl: " + websiteUrl - + "\n userExtensionsFile: " + userExtensionsFile - + "\n firefoxProfileDir: " + firefoxProfileDir - + "\n suiteTimeoutLimit: " + suiteTimeoutLimit - + "\n seleniumJarPath: " + seleniumJarPath - + "\n uploadDirectory: " + uploadDirectory - + "\n outputDirectory: " + outputDirectory - + "\n suiteParentDirectories: " + suiteParentDirectories - + "\n modelCleanerProperties: " + modelCleanerProperties - + "\n" + ignoredTests + "\n cleanModel: " + cleanModel - + "\n cleanUploads: " + cleanUploads; - } - - /** - * Look inside this parent directory and find any suite directories. You can - * recognize a suite directory because it contains a file named Suite.html. - */ - public Collection findSuiteDirs(File parentDir) { - return Arrays.asList(parentDir.listFiles(new FileFilter() { - public boolean accept(File pathname) { - if (!pathname.isDirectory()) { - return false; - } - if (pathname.getName().charAt(0) == '.') { - return false; - } - - File suiteFile = new File(pathname, "Suite.html"); - if (suiteFile.exists()) { - return true; - } else { - listener.subProcessErrout("Warning: suite file '" - + suiteFile.getPath() + "' does not exist.\n"); - return false; - } - } - })); - } - -} +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.utilities.testrunner; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener; +import edu.cornell.mannlib.vitro.utilities.testrunner.listener.LoggingListener; +import edu.cornell.mannlib.vitro.utilities.testrunner.listener.MulticastListener; + +/** + * Holds the runtime parameters that are read from the properties file, perhaps + * with modifications from the GUI if we are running interactively. + */ +public class SeleniumRunnerParameters { + public static final String PROP_OUTPUT_DIRECTORY = "output_directory"; + public static final String PROP_UPLOAD_DIRECTORY = "upload_directory"; + public static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories"; + public static final String PROP_WEBSITE_URL = "website_url"; + public static final String PROP_USER_EXTENSIONS_PATH = "user_extensions_path"; + public static final String PROP_FIREFOX_PROFILE_PATH = "firefox_profile_template_path"; + public static final String PROP_SUITE_TIMEOUT_LIMIT = "suite_timeout_limit"; + public static final String PROP_SELENIUM_JAR_PATH = "selenium_jar_path"; + public static final String PROP_IGNORED_TESTS = "ignored_tests_file"; + public static final String PROP_SUMMARY_CSS = "summary_css_file"; + + public static final String LOGFILE_NAME = "log_file.txt"; + + private final String websiteUrl; + private final File userExtensionsFile; + private final File firefoxProfileDir; + private final int suiteTimeoutLimit; + private final File seleniumJarPath; + private final File uploadDirectory; + private final File outputDirectory; + private final File logFile; + private final Collection suiteParentDirectories; + private final ModelCleanerProperties modelCleanerProperties; + private final IgnoredTests ignoredTests; + + private boolean cleanModel = true; + private boolean cleanUploads = true; + + // If we fail during the parameter parsing, we'll still write the log + // somewhere. + private Listener listener = new LoggingListener(System.out); + + /** + * Read the required properties from the property file, and do some checks + * on them. + */ + public SeleniumRunnerParameters(String propertiesFilepath) + throws IOException { + Properties props = loadPropertiesFile(propertiesFilepath); + + this.websiteUrl = getRequiredProperty(props, PROP_WEBSITE_URL); + this.userExtensionsFile = checkReadableFile(props, + PROP_USER_EXTENSIONS_PATH); + this.firefoxProfileDir = checkOptionalReadableDirectory(props, + PROP_FIREFOX_PROFILE_PATH); + this.suiteTimeoutLimit = getRequiredIntegerProperty(props, + PROP_SUITE_TIMEOUT_LIMIT); + this.seleniumJarPath = checkReadableFile(props, PROP_SELENIUM_JAR_PATH); + this.uploadDirectory = checkReadWriteDirectory(props, + PROP_UPLOAD_DIRECTORY); + + this.outputDirectory = checkOutputDirectory(props); + this.logFile = new File(this.outputDirectory, LOGFILE_NAME); + this.listener = new MulticastListener(); + addListener(new LoggingListener(this.logFile)); + + this.suiteParentDirectories = checkSuiteParentDirectories(props); + + this.modelCleanerProperties = new ModelCleanerProperties(props); + + // Get the list of ignored tests. + String ignoredFilesPath = getRequiredProperty(props, PROP_IGNORED_TESTS); + File ignoredFilesFile = new File(ignoredFilesPath); + FileHelper.checkReadableFile(ignoredFilesFile, "File '" + + ignoredFilesPath + "'"); + this.ignoredTests = new IgnoredTests(ignoredFilesFile); + } + + /** + * Load the properties from the properties file. + */ + private Properties loadPropertiesFile(String propertiesFilepath) + throws FileNotFoundException, IOException { + File propsFile = new File(propertiesFilepath); + if (!propsFile.exists()) { + throw new FileNotFoundException("Property file does not exist: '" + + propsFile + "'"); + } + + Reader propsReader = null; + try { + propsReader = new FileReader(propsFile); + Properties props = new Properties(); + props.load(propsReader); + return props; + } finally { + if (propsReader != null) { + try { + propsReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * If there is a parameter for this key, it should point to a readable + * directory. + */ + private File checkOptionalReadableDirectory(Properties props, String key) { + String value = props.getProperty(key); + if (value == null) { + return null; + } + + value = value.trim(); + if (value.trim().length() == 0) { + return null; + } + + File dir = new File(value); + + if (!dir.exists()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' does not exist."); + } + + if (!dir.isDirectory()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' is not a directory."); + } + + if (!dir.canRead()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' is not readable."); + } + + return dir; + } + + /** + * Check that there is a property for the required directory path, and that + * it points to a valid directory. + */ + private File checkReadWriteDirectory(Properties props, String key) { + String value = getRequiredProperty(props, key); + + File dir = new File(value); + + if (!dir.exists()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' does not exist. (" + dir.getAbsolutePath() + + ")"); + } + + if (!dir.isDirectory()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' is not a directory. (" + dir.getAbsolutePath() + + ")"); + } + + if (!dir.canRead()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' is not readable. (" + dir.getAbsolutePath() + + ")"); + } + + if (!dir.canWrite()) { + throw new IllegalArgumentException("Directory " + key + " '" + + value + "' is not writeable. (" + dir.getAbsolutePath() + + ")"); + } + return dir; + } + + private File checkReadableFile(Properties props, String key) { + String value = getRequiredProperty(props, key); + + File file = new File(value); + + if (!file.exists()) { + throw new IllegalArgumentException("File " + key + ": '" + value + + "' does not exist. (" + file.getAbsolutePath() + ")"); + } + if (!file.isFile()) { + throw new IllegalArgumentException("File " + key + ": '" + value + + "' is not a file. (" + file.getAbsolutePath() + ")"); + } + if (!file.canRead()) { + throw new IllegalArgumentException("File " + key + ": '" + value + + "' is not readable. (" + file.getAbsolutePath() + ")"); + } + return file; + } + + /** + * Get the property for the output directory. If it does not exist, create + * it (the parent must exist). Ensure that it is writeable. + */ + private File checkOutputDirectory(Properties props) throws IOException { + String value = getRequiredProperty(props, PROP_OUTPUT_DIRECTORY); + File outputDirectory = new File(value); + File outputParent = outputDirectory.getParentFile(); + + if (!outputDirectory.exists()) { + if (!outputParent.exists()) { + throw new IllegalArgumentException( + "Output directory does not exist, nor does its parent. '" + + outputDirectory + "' (" + + outputDirectory.getAbsolutePath() + ")"); + } + outputDirectory.mkdir(); + if (!outputDirectory.exists()) { + throw new IOException("Failed to create output directory: '" + + outputDirectory + "' (" + + outputDirectory.getAbsolutePath() + ")"); + } + } + + if (!outputDirectory.isDirectory()) { + throw new IllegalArgumentException("Suite directory '" + + outputDirectory.getPath() + "' is not a directory. (" + + outputDirectory.getAbsolutePath() + ")"); + } + if (!outputDirectory.canRead()) { + throw new IllegalArgumentException("Suite directory '" + + outputDirectory.getPath() + "' is not readable. (" + + outputDirectory.getAbsolutePath() + ")"); + } + if (!outputDirectory.canWrite()) { + throw new IllegalArgumentException("Suite directory '" + + outputDirectory.getPath() + "' is not writeable. (" + + outputDirectory.getAbsolutePath() + ")"); + } + + return outputDirectory; + } + + /** + * Get the property for the suite directories and ensure that each one is + * indeed a readable directory. + */ + private Collection checkSuiteParentDirectories(Properties props) { + String value = getRequiredProperty(props, PROP_SUITE_DIRECTORIES); + + List dirs = new ArrayList(); + String[] paths = value.split("[:;]"); + for (String path : paths) { + File dir = new File(path.trim()); + + if (!dir.exists()) { + throw new IllegalArgumentException("Suite directory '" + + dir.getPath() + "' does not exist. (" + + dir.getAbsolutePath() + ")"); + } + if (!dir.isDirectory()) { + throw new IllegalArgumentException("Suite directory '" + + dir.getPath() + "' is not a directory. (" + + dir.getAbsolutePath() + ")"); + } + if (!dir.canRead()) { + throw new IllegalArgumentException("Suite directory '" + + dir.getPath() + "' is not readable. (" + + dir.getAbsolutePath() + ")"); + } + dirs.add(dir); + } + return dirs; + } + + /** + * Get the value for this property. If there isn't one, or if it's empty, + * complain. + */ + private String getRequiredProperty(Properties props, String key) { + String value = props.getProperty(key); + if ((value == null) || (value.trim().length() == 0)) { + throw new IllegalArgumentException( + "Property file must provide a value for '" + key + "'"); + } + return value; + } + + /** + * This required property must be a valid integer. + */ + private int getRequiredIntegerProperty(Properties props, String key) { + String value = getRequiredProperty(props, key); + try { + return Integer.parseInt(value.trim()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Property value for '" + key + + "' is not a valid integer: " + value); + } + } + + public String getWebsiteUrl() { + return websiteUrl; + } + + public File getUserExtensionsFile() { + return userExtensionsFile; + } + + public boolean hasFirefoxProfileDir() { + return firefoxProfileDir != null; + } + + public File getFirefoxProfileDir() { + return firefoxProfileDir; + } + + public int getSuiteTimeoutLimit() { + return suiteTimeoutLimit; + } + + public File getSeleniumJarPath() { + return seleniumJarPath; + } + + public void addListener(Listener l) { + if (listener instanceof MulticastListener) { + ((MulticastListener) listener).addListener(l); + } else { + throw new IllegalStateException("Listener is not a multi-cast -- " + + "can't add new listeners."); + } + } + + public Listener getListener() { + return listener; + } + + public void setListener(Listener logger) { + this.listener = logger; + } + + public File getUploadDirectory() { + return uploadDirectory; + } + + public File getOutputDirectory() { + return outputDirectory; + } + + public File getLogFile() { + return logFile; + } + + public Collection getSuiteParentDirectories() { + return suiteParentDirectories; + } + + public ModelCleanerProperties getModelCleanerProperties() { + return modelCleanerProperties; + } + + public IgnoredTests getIgnoredTests() { + return ignoredTests; + } + + public boolean isCleanModel() { + return cleanModel; + } + + public void setCleanModel(boolean cleanModel) { + this.cleanModel = cleanModel; + } + + public boolean isCleanUploads() { + return cleanUploads; + } + + public void setCleanUploads(boolean cleanUploads) { + this.cleanUploads = cleanUploads; + } + + public String toString() { + return "Parameters:" + "\n websiteUrl: " + websiteUrl + + "\n userExtensionsFile: " + userExtensionsFile + + "\n firefoxProfileDir: " + firefoxProfileDir + + "\n suiteTimeoutLimit: " + suiteTimeoutLimit + + "\n seleniumJarPath: " + seleniumJarPath + + "\n uploadDirectory: " + uploadDirectory + + "\n outputDirectory: " + outputDirectory + + "\n suiteParentDirectories: " + suiteParentDirectories + + "\n modelCleanerProperties: " + modelCleanerProperties + + "\n" + ignoredTests + "\n cleanModel: " + cleanModel + + "\n cleanUploads: " + cleanUploads; + } + + /** + * Look inside this parent directory and find any suite directories. You can + * recognize a suite directory because it contains a file named Suite.html. + */ + public Collection findSuiteDirs(File parentDir) { + return Arrays.asList(parentDir.listFiles(new FileFilter() { + public boolean accept(File pathname) { + if (!pathname.isDirectory()) { + return false; + } + if (pathname.getName().charAt(0) == '.') { + return false; + } + + File suiteFile = new File(pathname, "Suite.html"); + if (suiteFile.exists()) { + return true; + } else { + listener.subProcessErrout("Warning: suite file '" + + suiteFile.getPath() + "' does not exist.\n"); + return false; + } + } + })); + } + +} diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/datamodel/DataModel.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/datamodel/DataModel.java index a02f970be..fc6689f5e 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/datamodel/DataModel.java +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/datamodel/DataModel.java @@ -1,294 +1,294 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests; -import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; -import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; -import edu.cornell.mannlib.vitro.utilities.testrunner.Status; -import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData; -import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener; -import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; -import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults; - -/** - * Collect all that we know about suites, tests, and their current status. - */ -public class DataModel { - - /* base data */ - private Collection suiteContents = Collections.emptyList(); - private Collection selectedSuites = Collections.emptyList(); - private Collection suiteResults = Collections.emptyList(); - private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO; - private IgnoredTests ignoredTestList = IgnoredTests.EMPTY_LIST; - private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO - - /* derived data */ - private Status runStatus = Status.PENDING; - - private final SortedMap suiteDataMap = new TreeMap(); - private final EnumMap> suiteMapByStatus = new EnumMap>( - Status.class); - - private final List allTests = new ArrayList(); - private final EnumMap> testMapByStatus = new EnumMap>( - Status.class); - - // ---------------------------------------------------------------------- - // Constructor - // ---------------------------------------------------------------------- - - public DataModel() { - calculate(); - } - - // ---------------------------------------------------------------------- - // Update the base data. - // ---------------------------------------------------------------------- - - public void setSuiteContents(Collection suiteContents) { - this.suiteContents = new ArrayList(suiteContents); - calculate(); - } - - public void setSelectedSuites(Collection selectedSuites) { - this.selectedSuites = new ArrayList(selectedSuites); - calculate(); - } - - public Collection getSelectedSuites() { - return new ArrayList(selectedSuites); - } - - public void setSuiteResults(Collection suiteResults) { - this.suiteResults = new ArrayList(suiteResults); - calculate(); - } - - public void captureDataListener(OutputDataListener dataListener) { - this.dataListenerInfo = dataListener.getInfo(); - calculate(); - } - - public void setIgnoredTestList(IgnoredTests ignoredTestList) { - this.ignoredTestList = ignoredTestList; - calculate(); - } - - public void setLogStats(LogStats logStats) { // TODO - this.logStats = logStats; - calculate(); - } - - // ---------------------------------------------------------------------- - // Keep the derived data current. - // ---------------------------------------------------------------------- - - /** - * Data in the model has been updated. Refresh all derived data. - */ - private void calculate() { - // Clear all derived data. - runStatus = Status.OK; - - suiteDataMap.clear(); - suiteMapByStatus.clear(); - for (Status s : Status.values()) { - suiteMapByStatus.put(s, new ArrayList()); - } - - allTests.clear(); - testMapByStatus.clear(); - for (Status s : Status.values()) { - testMapByStatus.put(s, new ArrayList()); - } - - /* - * Populate the Suite map with all Suites. - */ - Map resultsMap = new HashMap(); - for (SuiteResults result : suiteResults) { - resultsMap.put(result.getName(), result); - } - Map contentsMap = new HashMap(); - for (SuiteContents contents : suiteContents) { - contentsMap.put(contents.getName(), contents); - } - - for (SuiteContents contents : suiteContents) { - String name = contents.getName(); - SuiteResults result = resultsMap.get(name); - boolean ignored = ignoredTestList.isIgnored(name); - ProcessOutput failureMessages = dataListenerInfo - .getFailureMessages().get(name); - suiteDataMap.put(name, new SuiteData(name, ignored, contents, - result, failureMessages)); - } - - /* - * Map the Suites by status. - */ - for (SuiteData s : suiteDataMap.values()) { - getSuites(s.getStatus()).add(s); - } - - /** - * Populate the Test map with all Tests, and map by status. - */ - for (SuiteData s : suiteDataMap.values()) { - for (TestData t : s.getTestMap().values()) { - allTests.add(t); - getTests(t.getStatus()).add(t); - } - } - - if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) { - runStatus = Status.ERROR; - } else if (!getSuites(Status.PENDING).isEmpty()) { - runStatus = Status.PENDING; - } else { - runStatus = Status.OK; - } - } - - // ---------------------------------------------------------------------- - // Access the derived data. - // ---------------------------------------------------------------------- - - public Status getRunStatus() { - return runStatus; - } - - public long getStartTime() { - return dataListenerInfo.getStartTime(); - } - - public long getEndTime() { - return dataListenerInfo.getEndTime(); - } - - public long getElapsedTime() { - return dataListenerInfo.getElapsedTime(); - } - - public boolean isAnyPasses() { - return !getTests(Status.OK).isEmpty(); - } - - public boolean isAnyFailures() { - return !getTests(Status.ERROR).isEmpty(); - } - - public boolean isAnyIgnores() { - return !getTests(Status.IGNORED).isEmpty(); - } - - public boolean isAnyPending() { - return !getTests(Status.PENDING).isEmpty(); - } - - public int getTotalSuiteCount() { - return suiteDataMap.size(); - } - - public int getPassingSuiteCount() { - return getSuites(Status.OK).size(); - } - - public int getFailingSuiteCount() { - return getSuites(Status.ERROR).size(); - } - - public int getIgnoredSuiteCount() { - return getSuites(Status.IGNORED).size(); - } - - public int getPendingSuiteCount() { - return getSuites(Status.PENDING).size(); - } - - public Collection getAllSuites() { - return suiteDataMap.values(); - } - - public Map getSuitesWithFailureMessages() { - Map map = new TreeMap(); - for (SuiteData s : suiteDataMap.values()) { - if (s.getFailureMessages() != null) { - map.put(s.getName(), s); - } - } - return map; - } - - public int getTotalTestCount() { - return allTests.size(); - } - - public int getPassingTestCount() { - return getTests(Status.OK).size(); - } - - public int getFailingTestCount() { - return getTests(Status.ERROR).size(); - } - - public int getIgnoredTestCount() { - return getTests(Status.IGNORED).size(); - } - - public int getPendingTestCount() { - return getTests(Status.PENDING).size(); - } - - public Collection getAllTests() { - return Collections.unmodifiableCollection(allTests); - } - - public Collection getFailingTests() { - return Collections.unmodifiableCollection(getTests(Status.ERROR)); - } - - public Collection getIgnoredTests() { - return Collections.unmodifiableCollection(getTests(Status.IGNORED)); - } - - public Collection getIgnoredTestInfo() { - return ignoredTestList.getList(); - } - - public String getReasonForIgnoring(String suiteName, String testName) { - return ignoredTestList.getReasonForIgnoring(suiteName, testName); - } - - // ---------------------------------------------------------------------- - // Helper methods - // ---------------------------------------------------------------------- - - /** - * Get the list of suites that have this status. - */ - private List getSuites(Status st) { - return suiteMapByStatus.get(st); - } - - /** - * Get the list of tests that have this status. - */ - private List getTests(Status st) { - return testMapByStatus.get(st); - } - -} +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests; +import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; +import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; +import edu.cornell.mannlib.vitro.utilities.testrunner.Status; +import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData; +import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener; +import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; +import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults; + +/** + * Collect all that we know about suites, tests, and their current status. + */ +public class DataModel { + + /* base data */ + private Collection suiteContents = Collections.emptyList(); + private Collection selectedSuites = Collections.emptyList(); + private Collection suiteResults = Collections.emptyList(); + private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO; + private IgnoredTests ignoredTestList = IgnoredTests.EMPTY_LIST; + private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO + + /* derived data */ + private Status runStatus = Status.PENDING; + + private final SortedMap suiteDataMap = new TreeMap(); + private final EnumMap> suiteMapByStatus = new EnumMap>( + Status.class); + + private final List allTests = new ArrayList(); + private final EnumMap> testMapByStatus = new EnumMap>( + Status.class); + + // ---------------------------------------------------------------------- + // Constructor + // ---------------------------------------------------------------------- + + public DataModel() { + calculate(); + } + + // ---------------------------------------------------------------------- + // Update the base data. + // ---------------------------------------------------------------------- + + public void setSuiteContents(Collection suiteContents) { + this.suiteContents = new ArrayList(suiteContents); + calculate(); + } + + public void setSelectedSuites(Collection selectedSuites) { + this.selectedSuites = new ArrayList(selectedSuites); + calculate(); + } + + public Collection getSelectedSuites() { + return new ArrayList(selectedSuites); + } + + public void setSuiteResults(Collection suiteResults) { + this.suiteResults = new ArrayList(suiteResults); + calculate(); + } + + public void captureDataListener(OutputDataListener dataListener) { + this.dataListenerInfo = dataListener.getInfo(); + calculate(); + } + + public void setIgnoredTestList(IgnoredTests ignoredTestList) { + this.ignoredTestList = ignoredTestList; + calculate(); + } + + public void setLogStats(LogStats logStats) { // TODO + this.logStats = logStats; + calculate(); + } + + // ---------------------------------------------------------------------- + // Keep the derived data current. + // ---------------------------------------------------------------------- + + /** + * Data in the model has been updated. Refresh all derived data. + */ + private void calculate() { + // Clear all derived data. + runStatus = Status.OK; + + suiteDataMap.clear(); + suiteMapByStatus.clear(); + for (Status s : Status.values()) { + suiteMapByStatus.put(s, new ArrayList()); + } + + allTests.clear(); + testMapByStatus.clear(); + for (Status s : Status.values()) { + testMapByStatus.put(s, new ArrayList()); + } + + /* + * Populate the Suite map with all Suites. + */ + Map resultsMap = new HashMap(); + for (SuiteResults result : suiteResults) { + resultsMap.put(result.getName(), result); + } + Map contentsMap = new HashMap(); + for (SuiteContents contents : suiteContents) { + contentsMap.put(contents.getName(), contents); + } + + for (SuiteContents contents : suiteContents) { + String name = contents.getName(); + SuiteResults result = resultsMap.get(name); + boolean ignored = ignoredTestList.isIgnored(name); + ProcessOutput failureMessages = dataListenerInfo + .getFailureMessages().get(name); + suiteDataMap.put(name, new SuiteData(name, ignored, contents, + result, failureMessages)); + } + + /* + * Map the Suites by status. + */ + for (SuiteData s : suiteDataMap.values()) { + getSuites(s.getStatus()).add(s); + } + + /** + * Populate the Test map with all Tests, and map by status. + */ + for (SuiteData s : suiteDataMap.values()) { + for (TestData t : s.getTestMap().values()) { + allTests.add(t); + getTests(t.getStatus()).add(t); + } + } + + if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) { + runStatus = Status.ERROR; + } else if (!getSuites(Status.PENDING).isEmpty()) { + runStatus = Status.PENDING; + } else { + runStatus = Status.OK; + } + } + + // ---------------------------------------------------------------------- + // Access the derived data. + // ---------------------------------------------------------------------- + + public Status getRunStatus() { + return runStatus; + } + + public long getStartTime() { + return dataListenerInfo.getStartTime(); + } + + public long getEndTime() { + return dataListenerInfo.getEndTime(); + } + + public long getElapsedTime() { + return dataListenerInfo.getElapsedTime(); + } + + public boolean isAnyPasses() { + return !getTests(Status.OK).isEmpty(); + } + + public boolean isAnyFailures() { + return !getTests(Status.ERROR).isEmpty(); + } + + public boolean isAnyIgnores() { + return !getTests(Status.IGNORED).isEmpty(); + } + + public boolean isAnyPending() { + return !getTests(Status.PENDING).isEmpty(); + } + + public int getTotalSuiteCount() { + return suiteDataMap.size(); + } + + public int getPassingSuiteCount() { + return getSuites(Status.OK).size(); + } + + public int getFailingSuiteCount() { + return getSuites(Status.ERROR).size(); + } + + public int getIgnoredSuiteCount() { + return getSuites(Status.IGNORED).size(); + } + + public int getPendingSuiteCount() { + return getSuites(Status.PENDING).size(); + } + + public Collection getAllSuites() { + return suiteDataMap.values(); + } + + public Map getSuitesWithFailureMessages() { + Map map = new TreeMap(); + for (SuiteData s : suiteDataMap.values()) { + if (s.getFailureMessages() != null) { + map.put(s.getName(), s); + } + } + return map; + } + + public int getTotalTestCount() { + return allTests.size(); + } + + public int getPassingTestCount() { + return getTests(Status.OK).size(); + } + + public int getFailingTestCount() { + return getTests(Status.ERROR).size(); + } + + public int getIgnoredTestCount() { + return getTests(Status.IGNORED).size(); + } + + public int getPendingTestCount() { + return getTests(Status.PENDING).size(); + } + + public Collection getAllTests() { + return Collections.unmodifiableCollection(allTests); + } + + public Collection getFailingTests() { + return Collections.unmodifiableCollection(getTests(Status.ERROR)); + } + + public Collection getIgnoredTests() { + return Collections.unmodifiableCollection(getTests(Status.IGNORED)); + } + + public Collection getIgnoredTestInfo() { + return ignoredTestList.getList(); + } + + public String getReasonForIgnoring(String suiteName, String testName) { + return ignoredTestList.getReasonForIgnoring(suiteName, testName); + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + /** + * Get the list of suites that have this status. + */ + private List getSuites(Status st) { + return suiteMapByStatus.get(st); + } + + /** + * Get the list of tests that have this status. + */ + private List getTests(Status st) { + return testMapByStatus.get(st); + } + +} diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/output/OutputSummaryFormatter.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/output/OutputSummaryFormatter.java index 2d76e2929..815ae31ba 100644 --- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/output/OutputSummaryFormatter.java +++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/output/OutputSummaryFormatter.java @@ -1,375 +1,375 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.utilities.testrunner.output; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.Reader; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.Map; - -import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper; -import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; -import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; -import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters; -import edu.cornell.mannlib.vitro.utilities.testrunner.Status; -import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel; -import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData; -import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData; -import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; - -/** - * Creates the summary HTML file. - */ -public class OutputSummaryFormatter { - public static final String SUMMARY_HTML_FILENAME = "summary.html"; - public static final String SUMMARY_CSS_FILENAME = "summary.css"; - private final SimpleDateFormat dateFormat = new SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss"); - private final SeleniumRunnerParameters parms; - - private LogStats logStats; - private DataModel dataModel; - - public OutputSummaryFormatter(SeleniumRunnerParameters parms) { - this.parms = parms; - } - - /** - * Create a summary HTML file from the info contained in this log file and - * these suite outputs. - */ - public void format(LogStats logStats, DataModel dataModel) { - this.logStats = logStats; - this.dataModel = dataModel; - - PrintWriter writer = null; - try { - copyCssFile(); - - File outputFile = new File(parms.getOutputDirectory(), - SUMMARY_HTML_FILENAME); - writer = new PrintWriter(outputFile); - - writeHeader(writer); - writeStatsSection(writer); - writeErrorMessagesSection(writer); - writeCondensedTable(writer); - writeIgnoreSection(writer); - writeSuiteErrorMessagesSection(writer); - writeFooter(writer); - } catch (IOException e) { - // There is no appeal for any problems here. Just report them. - e.printStackTrace(); - } finally { - if (writer != null) { - writer.close(); - } - } - } - - /** - * Copy the CSS file into the output directory. - */ - private void copyCssFile() { - InputStream cssStream = this.getClass().getResourceAsStream( - SUMMARY_CSS_FILENAME); - if (cssStream == null) { - System.out.println("Couldn't find the CSS file: '" - + SUMMARY_CSS_FILENAME + "'"); - } else { - File cssTarget = new File(parms.getOutputDirectory(), - SUMMARY_CSS_FILENAME); - try { - FileHelper.copy(cssStream, cssTarget); - cssStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private void writeHeader(PrintWriter w) { - Status runStatus = dataModel.getRunStatus(); - String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS" - : runStatus.toString(); - String startString = formatDateTime(dataModel.getStartTime()); - - w.println(""); - w.println(""); - w.println(" Summary of Acceptance Tests " + startString - + ""); - w.println(" "); - w.println(""); - w.println(""); - w.println(); - w.println("
"); - w.println(" Acceptance test results: " + startString); - w.println("
" + statusString + "
"); - w.println("
"); - } - - private void writeStatsSection(PrintWriter w) { - String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass() - : ""; - String failClass = dataModel.isAnyFailures() ? Status.ERROR - .getHtmlClass() : ""; - String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED - .getHtmlClass() : ""; - - String start = formatDateTime(dataModel.getStartTime()); - String end = formatDateTime(dataModel.getEndTime()); - String elapsed = formatElapsedTime(dataModel.getElapsedTime()); - - w.println("
Summary
"); - w.println(); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - w.println("
"); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - w.println("
Start time:" + start - + "
End time:" + end + "
Elapsed time" + elapsed - + "
"); - w.println("
"); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - if (dataModel.isAnyPending()) { - w.println(" "); - } - w.println(" "); - w.println("
 SuitesTests
Passed" + dataModel.getPassingSuiteCount() - + "" + dataModel.getPassingTestCount() + "
Failed" + dataModel.getFailingSuiteCount() - + "" + dataModel.getFailingTestCount() + "
Ignored" + dataModel.getIgnoredSuiteCount() - + "" + dataModel.getIgnoredTestCount() + "
Pending" - + dataModel.getPendingSuiteCount() + "" - + dataModel.getPendingTestCount() + "
Total" - + dataModel.getTotalSuiteCount() + "" - + dataModel.getTotalTestCount() + "
"); - w.println("
"); - w.println(); - } - - private void writeErrorMessagesSection(PrintWriter w) { - String errorClass = Status.ERROR.getHtmlClass(); - - w.println("
Errors and warnings
"); - w.println(); - w.println(" "); - - if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) { - w.println(" "); - } else { - for (String e : logStats.getErrors()) { - w.println(" "); - } - } - w.println("
No errors or warnings
ERROR" + e + "
"); - w.println(); - } - - private void writeCondensedTable(PrintWriter w) { - w.println("
Condensed List
"); - w.println(); - w.println(" "); - for (SuiteData s : dataModel.getAllSuites()) { - String sReason = ""; - if (s.getStatus() == Status.IGNORED) { - sReason = dataModel.getReasonForIgnoring(s.getName(), "*"); - } else if (s.getFailureMessages() != null) { - sReason = s.getFailureMessages().getErrout(); - } else if (s.getStatus() == Status.PENDING) { - sReason = Status.PENDING.toString(); - } - - w.println(" "); - w.println(" "); - w.println(" "); - } - w.println("
"); - w.println("
" + outputLink(s) - + "
"); - if (!sReason.isEmpty()) { - // The entire class is either failed or pending or ignored. - w.println("
" + sReason + "
"); - } else { - // Show the individual tests. - for (TestData t : s.getTestMap().values()) { - String tClass = t.getStatus().getHtmlClass(); - String tReason = dataModel.getReasonForIgnoring( - s.getName(), t.getTestName()); - - w.println("
"); - w.println(" " + outputLink(t)); - if (!tReason.isEmpty()) { - w.println("
" + tReason - + "
"); - } - w.println("
"); - } - } - w.println("
"); - w.println(); - } - - private void writeSuiteErrorMessagesSection(PrintWriter w) { - Map failedSuiteMap = dataModel - .getSuitesWithFailureMessages(); - if (failedSuiteMap.isEmpty()) { - return; - } - - w.println("
All tests
"); - w.println(); - for (SuiteData s : failedSuiteMap.values()) { - ProcessOutput output = s.getFailureMessages(); - - w.println(" "); - w.println(" "); - w.println(" \n"); - w.println(" \n"); - w.println("
Standard Output
" + output.getStdout()
-					+ "
"); - w.println("
 
"); - - w.println(" "); - w.println(" \n"); - w.println(" \n"); - w.println("
Error Output
" + output.getErrout()
-					+ "
"); - w.println("
 
"); - w.println(); - } - } - - private void writeIgnoreSection(PrintWriter w) { - String warnClass = Status.IGNORED.getHtmlClass(); - Collection ignoredTests = dataModel - .getIgnoredTestInfo(); - - w.println("
Ignored
"); - w.println(); - w.println(" "); - w.println(" " - + "\n"); - if (ignoredTests.isEmpty()) { - w.println(" " - + ""); - } else { - for (IgnoredTestInfo info : ignoredTests) { - String suiteName = info.suiteName; - String testName = info.testName; - String reason = dataModel.getReasonForIgnoring(suiteName, - testName); - - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - w.println(" "); - } - } - w.println("
Suite nameTest nameReason for ignoring
No tests ignored.
" + suiteName + "" + testName + "" + reason + "
"); - w.println(); - } - - private void writeFooter(PrintWriter w) { - w.println("
Log
"); - w.println("
");
-
-		Reader reader = null;
-		try {
-			reader = new FileReader(parms.getLogFile());
-			char[] buffer = new char[4096];
-			int howMany;
-			while (-1 != (howMany = reader.read(buffer))) {
-				w.write(buffer, 0, howMany);
-			}
-		} catch (IOException e) {
-			e.printStackTrace();
-		} finally {
-			if (reader != null) {
-				try {
-					reader.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-
-		w.println("  
"); - w.println(""); - w.println(""); - } - - private String formatElapsedTime(long elapsed) { - if (elapsed == 0) { - return "---"; - } - - long elapsedSeconds = elapsed / 1000L; - long seconds = elapsedSeconds % 60L; - long elapsedMinutes = elapsedSeconds / 60L; - long minutes = elapsedMinutes % 60L; - long hours = elapsedMinutes / 60L; - - String elapsedTime = ""; - if (hours > 0) { - elapsedTime += hours + "h "; - } - if (minutes > 0 || hours > 0) { - elapsedTime += minutes + "m "; - } - elapsedTime += seconds + "s"; - - return elapsedTime; - } - - private String formatDateTime(long dateTime) { - if (dateTime == 0) { - return "---"; - } - - return dateFormat.format(new Date(dateTime)); - } - - private String outputLink(SuiteData s) { - if (s.getOutputLink() == null) { - return s.getName(); - } else { - return "
" + s.getName() - + ""; - } - } - - private String outputLink(TestData t) { - if (t.getOutputLink() == null) { - return t.getTestName(); - } else { - return "" + t.getTestName() - + ""; - } - } -} +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.utilities.testrunner.output; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper; +import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; +import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; +import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters; +import edu.cornell.mannlib.vitro.utilities.testrunner.Status; +import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel; +import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData; +import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData; +import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; + +/** + * Creates the summary HTML file. + */ +public class OutputSummaryFormatter { + public static final String SUMMARY_HTML_FILENAME = "summary.html"; + public static final String SUMMARY_CSS_FILENAME = "summary.css"; + private final SimpleDateFormat dateFormat = new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + private final SeleniumRunnerParameters parms; + + private LogStats logStats; + private DataModel dataModel; + + public OutputSummaryFormatter(SeleniumRunnerParameters parms) { + this.parms = parms; + } + + /** + * Create a summary HTML file from the info contained in this log file and + * these suite outputs. + */ + public void format(LogStats logStats, DataModel dataModel) { + this.logStats = logStats; + this.dataModel = dataModel; + + PrintWriter writer = null; + try { + copyCssFile(); + + File outputFile = new File(parms.getOutputDirectory(), + SUMMARY_HTML_FILENAME); + writer = new PrintWriter(outputFile); + + writeHeader(writer); + writeStatsSection(writer); + writeErrorMessagesSection(writer); + writeCondensedTable(writer); + writeIgnoreSection(writer); + writeSuiteErrorMessagesSection(writer); + writeFooter(writer); + } catch (IOException e) { + // There is no appeal for any problems here. Just report them. + e.printStackTrace(); + } finally { + if (writer != null) { + writer.close(); + } + } + } + + /** + * Copy the CSS file into the output directory. + */ + private void copyCssFile() { + InputStream cssStream = this.getClass().getResourceAsStream( + SUMMARY_CSS_FILENAME); + if (cssStream == null) { + System.out.println("Couldn't find the CSS file: '" + + SUMMARY_CSS_FILENAME + "'"); + } else { + File cssTarget = new File(parms.getOutputDirectory(), + SUMMARY_CSS_FILENAME); + try { + FileHelper.copy(cssStream, cssTarget); + cssStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void writeHeader(PrintWriter w) { + Status runStatus = dataModel.getRunStatus(); + String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS" + : runStatus.toString(); + String startString = formatDateTime(dataModel.getStartTime()); + + w.println(""); + w.println(""); + w.println(" Summary of Acceptance Tests " + startString + + ""); + w.println(" "); + w.println(""); + w.println(""); + w.println(); + w.println("
"); + w.println(" Acceptance test results: " + startString); + w.println("
" + statusString + "
"); + w.println("
"); + } + + private void writeStatsSection(PrintWriter w) { + String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass() + : ""; + String failClass = dataModel.isAnyFailures() ? Status.ERROR + .getHtmlClass() : ""; + String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED + .getHtmlClass() : ""; + + String start = formatDateTime(dataModel.getStartTime()); + String end = formatDateTime(dataModel.getEndTime()); + String elapsed = formatElapsedTime(dataModel.getElapsedTime()); + + w.println("
Summary
"); + w.println(); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + w.println("
"); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + w.println("
Start time:" + start + + "
End time:" + end + "
Elapsed time" + elapsed + + "
"); + w.println("
"); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + if (dataModel.isAnyPending()) { + w.println(" "); + } + w.println(" "); + w.println("
 SuitesTests
Passed" + dataModel.getPassingSuiteCount() + + "" + dataModel.getPassingTestCount() + "
Failed" + dataModel.getFailingSuiteCount() + + "" + dataModel.getFailingTestCount() + "
Ignored" + dataModel.getIgnoredSuiteCount() + + "" + dataModel.getIgnoredTestCount() + "
Pending" + + dataModel.getPendingSuiteCount() + "" + + dataModel.getPendingTestCount() + "
Total" + + dataModel.getTotalSuiteCount() + "" + + dataModel.getTotalTestCount() + "
"); + w.println("
"); + w.println(); + } + + private void writeErrorMessagesSection(PrintWriter w) { + String errorClass = Status.ERROR.getHtmlClass(); + + w.println("
Errors and warnings
"); + w.println(); + w.println(" "); + + if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) { + w.println(" "); + } else { + for (String e : logStats.getErrors()) { + w.println(" "); + } + } + w.println("
No errors or warnings
ERROR" + e + "
"); + w.println(); + } + + private void writeCondensedTable(PrintWriter w) { + w.println("
Condensed List
"); + w.println(); + w.println(" "); + for (SuiteData s : dataModel.getAllSuites()) { + String sReason = ""; + if (s.getStatus() == Status.IGNORED) { + sReason = dataModel.getReasonForIgnoring(s.getName(), "*"); + } else if (s.getFailureMessages() != null) { + sReason = s.getFailureMessages().getErrout(); + } else if (s.getStatus() == Status.PENDING) { + sReason = Status.PENDING.toString(); + } + + w.println(" "); + w.println(" "); + w.println(" "); + } + w.println("
"); + w.println("
" + outputLink(s) + + "
"); + if (!sReason.isEmpty()) { + // The entire class is either failed or pending or ignored. + w.println("
" + sReason + "
"); + } else { + // Show the individual tests. + for (TestData t : s.getTestMap().values()) { + String tClass = t.getStatus().getHtmlClass(); + String tReason = dataModel.getReasonForIgnoring( + s.getName(), t.getTestName()); + + w.println("
"); + w.println(" " + outputLink(t)); + if (!tReason.isEmpty()) { + w.println("
" + tReason + + "
"); + } + w.println("
"); + } + } + w.println("
"); + w.println(); + } + + private void writeSuiteErrorMessagesSection(PrintWriter w) { + Map failedSuiteMap = dataModel + .getSuitesWithFailureMessages(); + if (failedSuiteMap.isEmpty()) { + return; + } + + w.println("
All tests
"); + w.println(); + for (SuiteData s : failedSuiteMap.values()) { + ProcessOutput output = s.getFailureMessages(); + + w.println(" "); + w.println(" "); + w.println(" \n"); + w.println(" \n"); + w.println("
Standard Output
" + output.getStdout()
+					+ "
"); + w.println("
 
"); + + w.println(" "); + w.println(" \n"); + w.println(" \n"); + w.println("
Error Output
" + output.getErrout()
+					+ "
"); + w.println("
 
"); + w.println(); + } + } + + private void writeIgnoreSection(PrintWriter w) { + String warnClass = Status.IGNORED.getHtmlClass(); + Collection ignoredTests = dataModel + .getIgnoredTestInfo(); + + w.println("
Ignored
"); + w.println(); + w.println(" "); + w.println(" " + + "\n"); + if (ignoredTests.isEmpty()) { + w.println(" " + + ""); + } else { + for (IgnoredTestInfo info : ignoredTests) { + String suiteName = info.suiteName; + String testName = info.testName; + String reason = dataModel.getReasonForIgnoring(suiteName, + testName); + + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + w.println(" "); + } + } + w.println("
Suite nameTest nameReason for ignoring
No tests ignored.
" + suiteName + "" + testName + "" + reason + "
"); + w.println(); + } + + private void writeFooter(PrintWriter w) { + w.println("
Log
"); + w.println("
");
+
+		Reader reader = null;
+		try {
+			reader = new FileReader(parms.getLogFile());
+			char[] buffer = new char[4096];
+			int howMany;
+			while (-1 != (howMany = reader.read(buffer))) {
+				w.write(buffer, 0, howMany);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (reader != null) {
+				try {
+					reader.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+
+		w.println("  
"); + w.println(""); + w.println(""); + } + + private String formatElapsedTime(long elapsed) { + if (elapsed == 0) { + return "---"; + } + + long elapsedSeconds = elapsed / 1000L; + long seconds = elapsedSeconds % 60L; + long elapsedMinutes = elapsedSeconds / 60L; + long minutes = elapsedMinutes % 60L; + long hours = elapsedMinutes / 60L; + + String elapsedTime = ""; + if (hours > 0) { + elapsedTime += hours + "h "; + } + if (minutes > 0 || hours > 0) { + elapsedTime += minutes + "m "; + } + elapsedTime += seconds + "s"; + + return elapsedTime; + } + + private String formatDateTime(long dateTime) { + if (dateTime == 0) { + return "---"; + } + + return dateFormat.format(new Date(dateTime)); + } + + private String outputLink(SuiteData s) { + if (s.getOutputLink() == null) { + return s.getName(); + } else { + return "
" + s.getName() + + ""; + } + } + + private String outputLink(TestData t) { + if (t.getOutputLink() == null) { + return t.getTestName(); + } else { + return "" + t.getTestName() + + ""; + } + } +} From e2ad45f2cbfaaefdb25fd03175274c7bb6202993 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 20 Dec 2013 15:02:57 -0500 Subject: [PATCH 04/31] line-end characters --- .../utilities/testing/VitroTestRunner.java | 288 +++++++++--------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java b/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java index 76664d4e3..0c4f5ae2c 100644 --- a/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java +++ b/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java @@ -1,7 +1,7 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ -package edu.cornell.mannlib.vitro.utilities.testing; - +package edu.cornell.mannlib.vitro.utilities.testing; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -9,145 +9,145 @@ import java.util.SortedSet; import java.util.TreeSet; import org.junit.runner.JUnitCore; - -/** - * A Java application that will run the Vitro unit tests. It searches for unit - * tests in the supplied source directory (any file whose name is *Test.Java). - * It runs the tests with a variety of reporting detail, depending on the level - * selected. If the level selector is absent or unrecognized, the medium level - * is used. - * - * @author jeb228 - */ -public class VitroTestRunner { - public enum ReportLevel { - /** Report only the one-line summary. */ - BRIEF, - /** Report times and statistics for each test class. */ - MORE, - /** Report times and statistics for each test method. */ - FULL - } - - private final List> classes; - private final VitroTestRunListener listener; - - /** - * Locate the test classes. Initialize the test listener. - */ - public VitroTestRunner(File sourceRootDir, ReportLevel reportLevel) { - List classNames = getListOfTestClassNames(sourceRootDir); - this.classes = getTestClasses(classNames); - this.listener = new VitroTestRunListener(reportLevel); - } - - /** - * Start a recursive search through the source directory. - */ - private List getListOfTestClassNames(File sourceRootDir) { - SortedSet names = new TreeSet(); - searchForTestClasses(names, "", sourceRootDir); - return new ArrayList(names); - } - - /** - * Recursively search the directory for files in the form "*Test.java". - * Ignore any files or directories whose names start with a "." - */ - private void searchForTestClasses(SortedSet names, String prefix, - File directory) { - for (File file : directory.listFiles()) { - String filename = file.getName(); - if (filename.startsWith(".")) { - // ignore .svn, etc. - } else if (file.isDirectory()) { - searchForTestClasses(names, prefix + filename + ".", file); - } else if (filename.endsWith("Test.java")) { - String classname = filename.substring(0, filename.length() - 5); - names.add(prefix + classname); - } - } - } - - /** - * Instantiate a class for each test class name. - */ - private List> getTestClasses(List classNames) { - List> classes = new ArrayList>(); - for (String classname : classNames) { - try { - classes.add(Class.forName(classname)); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Can't load test class: " - + classname, e); - } - } - return classes; - } - - /** - * We've located all of the test clases. Now run them. - */ - private void run() { - JUnitCore junitCore = new JUnitCore(); - junitCore.addListener(this.listener); - junitCore.run(this.classes.toArray(new Class[0])); - } - - /** - * Did any of the tests fail? - */ - private boolean didEverythingPass() { - return this.listener.didEverythingPass(); - } - - /** - *

- * You must provide a path to the source directory of the test classes. - *

- *

- * You may also provide a reporting level of "BRIEF", "MORE", or "FULL". If - * no level is provided, or if it is not recognized, "BRIEF" is used. - *

- */ - public static void main(String[] args) { - if ((args.length < 1) || (args.length > 2)) { - usage("Wrong number of arguments: expecting 1 or 2, but found " - + args.length + "."); - } - File sourceRootDir = new File(args[0]); - - if (!sourceRootDir.exists()) { - usage(sourceRootDir + " does not exist."); - } - if (!sourceRootDir.isDirectory()) { - usage(sourceRootDir + " is not a directory."); - } - - ReportLevel reportLevel = ReportLevel.MORE; - if (args.length == 2) { - for (ReportLevel level : ReportLevel.values()) { - if (level.toString().equalsIgnoreCase(args[1])) { - reportLevel = level; - } - } - } - - VitroTestRunner runner = new VitroTestRunner(sourceRootDir, reportLevel); - runner.run(); - - if (!runner.didEverythingPass()) { - System.exit(1); - } - } - - /** - * Tell them how it should have been done. - */ - private static void usage(String message) { - System.out.println(message); - System.out.println("usage: " + VitroTestRunner.class.getSimpleName() - + " sourceRootDirectory [ BRIEF | MORE | FULL ]"); - System.exit(1); - } -} + +/** + * A Java application that will run the Vitro unit tests. It searches for unit + * tests in the supplied source directory (any file whose name is *Test.Java). + * It runs the tests with a variety of reporting detail, depending on the level + * selected. If the level selector is absent or unrecognized, the medium level + * is used. + * + * @author jeb228 + */ +public class VitroTestRunner { + public enum ReportLevel { + /** Report only the one-line summary. */ + BRIEF, + /** Report times and statistics for each test class. */ + MORE, + /** Report times and statistics for each test method. */ + FULL + } + + private final List> classes; + private final VitroTestRunListener listener; + + /** + * Locate the test classes. Initialize the test listener. + */ + public VitroTestRunner(File sourceRootDir, ReportLevel reportLevel) { + List classNames = getListOfTestClassNames(sourceRootDir); + this.classes = getTestClasses(classNames); + this.listener = new VitroTestRunListener(reportLevel); + } + + /** + * Start a recursive search through the source directory. + */ + private List getListOfTestClassNames(File sourceRootDir) { + SortedSet names = new TreeSet(); + searchForTestClasses(names, "", sourceRootDir); + return new ArrayList(names); + } + + /** + * Recursively search the directory for files in the form "*Test.java". + * Ignore any files or directories whose names start with a "." + */ + private void searchForTestClasses(SortedSet names, String prefix, + File directory) { + for (File file : directory.listFiles()) { + String filename = file.getName(); + if (filename.startsWith(".")) { + // ignore .svn, etc. + } else if (file.isDirectory()) { + searchForTestClasses(names, prefix + filename + ".", file); + } else if (filename.endsWith("Test.java")) { + String classname = filename.substring(0, filename.length() - 5); + names.add(prefix + classname); + } + } + } + + /** + * Instantiate a class for each test class name. + */ + private List> getTestClasses(List classNames) { + List> classes = new ArrayList>(); + for (String classname : classNames) { + try { + classes.add(Class.forName(classname)); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Can't load test class: " + + classname, e); + } + } + return classes; + } + + /** + * We've located all of the test clases. Now run them. + */ + private void run() { + JUnitCore junitCore = new JUnitCore(); + junitCore.addListener(this.listener); + junitCore.run(this.classes.toArray(new Class[0])); + } + + /** + * Did any of the tests fail? + */ + private boolean didEverythingPass() { + return this.listener.didEverythingPass(); + } + + /** + *

+ * You must provide a path to the source directory of the test classes. + *

+ *

+ * You may also provide a reporting level of "BRIEF", "MORE", or "FULL". If + * no level is provided, or if it is not recognized, "BRIEF" is used. + *

+ */ + public static void main(String[] args) { + if ((args.length < 1) || (args.length > 2)) { + usage("Wrong number of arguments: expecting 1 or 2, but found " + + args.length + "."); + } + File sourceRootDir = new File(args[0]); + + if (!sourceRootDir.exists()) { + usage(sourceRootDir + " does not exist."); + } + if (!sourceRootDir.isDirectory()) { + usage(sourceRootDir + " is not a directory."); + } + + ReportLevel reportLevel = ReportLevel.MORE; + if (args.length == 2) { + for (ReportLevel level : ReportLevel.values()) { + if (level.toString().equalsIgnoreCase(args[1])) { + reportLevel = level; + } + } + } + + VitroTestRunner runner = new VitroTestRunner(sourceRootDir, reportLevel); + runner.run(); + + if (!runner.didEverythingPass()) { + System.exit(1); + } + } + + /** + * Tell them how it should have been done. + */ + private static void usage(String message) { + System.out.println(message); + System.out.println("usage: " + VitroTestRunner.class.getSimpleName() + + " sourceRootDirectory [ BRIEF | MORE | FULL ]"); + System.exit(1); + } +} From b7b28cc896ec6cc76c60af62a62ec9c80960c212 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 20 Dec 2013 17:10:18 -0500 Subject: [PATCH 05/31] VIVO-617 Change the way we deal with Multipart requests. --- .../controller/MultipartRequestWrapper.java | 288 ++++++++++++++++++ .../webapp/controller/VitroHttpServlet.java | 25 +- .../vitro/webapp/controller/VitroRequest.java | 63 ++++ .../FileUploadServletRequest.java | 112 ------- .../MultipartHttpServletRequest.java | 261 ---------------- .../SimpleHttpServletRequestWrapper.java | 81 ----- 6 files changed, 373 insertions(+), 457 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/MultipartRequestWrapper.java delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/SimpleHttpServletRequestWrapper.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/MultipartRequestWrapper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/MultipartRequestWrapper.java new file mode 100644 index 000000000..02a7eee72 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/MultipartRequestWrapper.java @@ -0,0 +1,288 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Wraps a multipart HTTP request, and pre-parses it for file uploads, without + * losing the request parameters. + * + * Parsing through the request with an Apache ServletFileUpload builds a list of + * FileItems that includes the parameters and the file parts. After that, + * however, the parameters are no longer accessible from the request. This + * wrapper will see that they don't get lost. + * + * The List of FileItems includes both "formField" items and "file" items. As + * with the usual parameters on a request, we can have more than one value with + * the same name. So this creates a map of > to hold the + * parameters, and a map of > to hold the files. + * + * The parameters will be available to the wrapper through the normal methods. + * The files will be available as an attribute that holds the map. Also, a + * separate attribute will hold a Boolean to indicate that this was indeed a + * multipart request. + * + * Conveninence methods in VitroRequest will make these easy to handle, without + * actually touching the attributes. + */ +public class MultipartRequestWrapper extends HttpServletRequestWrapper { + private static final Log log = LogFactory + .getLog(MultipartRequestWrapper.class); + + private static final String CLASS_NAME = MultipartRequestWrapper.class + .getSimpleName(); + public static final String ATTRIBUTE_IS_MULTIPART = CLASS_NAME + + "_isMultipart"; + public static final String ATTRIBUTE_FILE_ITEM_MAP = CLASS_NAME + + "_fileItemMap"; + public static final String ATTRIBUTE_FILE_SIZE_EXCEPTION = CLASS_NAME + + "_fileSizeException"; + + private static final String[] EMPTY_ARRAY = new String[0]; + + // ---------------------------------------------------------------------- + // The factory + // ---------------------------------------------------------------------- + + /** + * If this is a multipart request, wrap it. Otherwise, just return the + * request as it is. + */ + public static HttpServletRequest parse(HttpServletRequest req, + ParsingStrategy strategy) throws IOException { + if (!ServletFileUpload.isMultipartContent(req)) { + return req; + } + + ListsMap parameters = new ListsMap<>(); + ListsMap files = new ListsMap<>(); + + parseQueryString(req.getQueryString(), parameters); + parseFileParts(req, parameters, files, strategy); + + return new MultipartRequestWrapper(req, parameters, files); + } + + /** + * Pull any parameters out of the URL. + */ + private static void parseQueryString(String queryString, + ListsMap parameters) { + log.debug("Query string is : '" + queryString + "'"); + if (queryString != null) { + String[] pieces = queryString.split("&"); + + for (String piece : pieces) { + int equalsHere = piece.indexOf('='); + if (piece.trim().isEmpty()) { + // Ignore an empty piece. + } else if (equalsHere <= 0) { + // A parameter without a value. + parameters.add(decode(piece), ""); + } else { + // A parameter with a value. + String key = piece.substring(0, equalsHere); + String value = piece.substring(equalsHere + 1); + parameters.add(decode(key), decode(value)); + } + } + } + log.debug("Parameters from query string are: " + parameters); + } + + /** + * Remove any special URL-style encoding. + */ + private static String decode(String encoded) { + try { + return URLDecoder.decode(encoded, "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error(e, e); + return encoded; + } + } + + private static void parseFileParts(HttpServletRequest req, + ListsMap parameters, ListsMap files, + ParsingStrategy strategy) throws IOException { + + ServletFileUpload upload = createUploadHandler(req, + strategy.maximumMultipartFileSize()); + List items = parseRequestIntoFileItems(req, upload, strategy); + + for (FileItem item : items) { + // Process a regular form field + String name = item.getFieldName(); + if (item.isFormField()) { + String value; + try { + value = item.getString("UTF-8"); + } catch (UnsupportedEncodingException e) { + value = item.getString(); + } + parameters.add(name, value); + log.debug("Form field (parameter) " + name + "=" + value); + } else { + files.add(name, item); + log.debug("File " + name + ": " + item.getSize() + " bytes."); + } + } + } + + /** + * Create an upload handler that will throw an exception if the file is too + * large. + */ + private static ServletFileUpload createUploadHandler( + HttpServletRequest req, long maxFileSize) { + File tempDir = figureTemporaryDirectory(req); + + DiskFileItemFactory factory = new DiskFileItemFactory(); + factory.setSizeThreshold(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD); + factory.setRepository(tempDir); + + ServletFileUpload upload = new ServletFileUpload(factory); + upload.setSizeMax(maxFileSize); + return upload; + } + + /** + * Find the temporary storage directory for this webapp. + */ + private static File figureTemporaryDirectory(HttpServletRequest request) { + return (File) request.getSession().getServletContext() + .getAttribute("javax.servlet.context.tempdir"); + } + + /** + * Parse the raw request into a list of parts. + * + * If there is a parsing error, let the strategy handle it. If the strategy + * throws it back, wrap it in an IOException and throw it on up. + */ + @SuppressWarnings("unchecked") + private static List parseRequestIntoFileItems( + HttpServletRequest req, ServletFileUpload upload, + ParsingStrategy strategy) throws IOException { + try { + return upload.parseRequest(req); + } catch (FileSizeLimitExceededException e) { + if (strategy.stashFileSizeException()) { + req.setAttribute(ATTRIBUTE_FILE_SIZE_EXCEPTION, e); + return Collections.emptyList(); + } else { + throw new IOException(e); + } + } catch (FileUploadException e) { + throw new IOException(e); + } + } + + // ---------------------------------------------------------------------- + // The instance + // ---------------------------------------------------------------------- + + private final ListsMap parameters; + + public MultipartRequestWrapper(HttpServletRequest req, + ListsMap parameters, ListsMap files) { + super(req); + + this.parameters = parameters; + + req.setAttribute(ATTRIBUTE_IS_MULTIPART, Boolean.TRUE); + req.setAttribute(ATTRIBUTE_FILE_ITEM_MAP, files); + } + + /** + * Look in the map of parsed parameters. + */ + @Override + public String getParameter(String name) { + if (parameters.containsKey(name)) { + return parameters.get(name).get(0); + } else { + return null; + } + } + + /** + * Look in the map of parsed parameters. Make a protective copy. + */ + @Override + public Enumeration getParameterNames() { + return Collections.enumeration(new HashSet<>(parameters.keySet())); + } + + /** + * Look in the map of parsed parameters. + */ + @Override + public String[] getParameterValues(String name) { + if (parameters.containsKey(name)) { + return parameters.get(name).toArray(EMPTY_ARRAY); + } else { + return null; + } + } + + /** + * Make a copy of the map of parsed parameters; + */ + @Override + public Map getParameterMap() { + Map result = new HashMap(); + for (String key : parameters.keySet()) { + result.put(key, parameters.get(key).toArray(EMPTY_ARRAY)); + } + return result; + } + + // ---------------------------------------------------------------------- + // Helper classes + // ---------------------------------------------------------------------- + + private static class ListsMap extends HashMap> { + void add(String key, T value) { + if (!containsKey(key)) { + put(key, new ArrayList()); + } + get(key).add(value); + } + } + + public interface ParsingStrategy { + long maximumMultipartFileSize(); + + /** + * Allows you to handle the exception in your code. + * + * Be aware that the multipart parameters have been lost, and that may + * include form fields. + */ + boolean stashFileSizeException(); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroHttpServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroHttpServlet.java index dcc80a040..bf082aa56 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroHttpServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroHttpServlet.java @@ -30,14 +30,13 @@ import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; -import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.beans.ResourceBean; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LogoutRedirector; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.i18n.I18n; -public class VitroHttpServlet extends HttpServlet { +public class VitroHttpServlet extends HttpServlet implements MultipartRequestWrapper.ParsingStrategy { private static final long serialVersionUID = 1L; protected static DateFormat publicDateFormat = new SimpleDateFormat( @@ -60,8 +59,9 @@ public class VitroHttpServlet extends HttpServlet { if ((req instanceof HttpServletRequest) && (resp instanceof HttpServletResponse)) { HttpServletRequest hreq = (HttpServletRequest) req; - HttpServletResponse hresp = (HttpServletResponse) resp; + hreq = MultipartRequestWrapper.parse(hreq, this); + if (log.isTraceEnabled()) { dumpRequestHeaders(hreq); } @@ -70,6 +70,25 @@ public class VitroHttpServlet extends HttpServlet { super.service(req, resp); } + /** + * Override this to change the maximum size of uploaded files in multipart + * requests. + */ + @Override + public long maximumMultipartFileSize() { + return 50 * 1024 * 1024; // default is 50 megabytes + } + + /** + * Override this to change the way that exceptions are handled when parsing + * a multipart request. Be aware that multipart parameters have been lost, + * and that may include form fields. + */ + @Override + public boolean stashFileSizeException() { + return false; + } + /** * doGet does nothing. */ diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java index 05c0fc158..9878fe0b9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java @@ -3,12 +3,20 @@ package edu.cornell.mannlib.vitro.webapp.controller; +import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_FILE_ITEM_MAP; +import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_FILE_SIZE_EXCEPTION; +import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_IS_MULTIPART; + import java.text.Collator; +import java.util.Collections; +import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -231,4 +239,59 @@ public class VitroRequest extends HttpServletRequestWrapper { public WebappDaoFactory getLanguageNeutralWebappDaoFactory() { return (WebappDaoFactory) getAttribute("languageNeutralWebappDaoFactory"); } + + // ---------------------------------------------------------------------- + // Deal with parsed multipart requests. + // ---------------------------------------------------------------------- + + public boolean isMultipart() { + return getAttribute(ATTRIBUTE_IS_MULTIPART) != null; + } + + @SuppressWarnings("unchecked") + public Map> getFiles() { + Map> map; + map = (Map>) getAttribute(ATTRIBUTE_FILE_ITEM_MAP); + if (map == null) { + return Collections.emptyMap(); + } else { + return map; + } + } + + /** + * There may be more than one file item with the given name. If the first + * one is empty (size is zero), keep looking for a non-empty one. + */ + public FileItem getFileItem(String name) { + Map> map = getFiles(); + List items = map.get(name); + if (items == null) { + return null; + } + for (FileItem item : items) { + if (item.getSize() > 0L) { + return item; + } + } + return null; + } + + /** + * If the uploaded file exceeded the maximum size, and if the strategy said + * to stash the exception, it will be stored as a request attribute. + */ + public boolean hasFileSizeException() { + return getFileSizeException() != null; + } + + public FileSizeLimitExceededException getFileSizeException() { + Object e = getAttribute(ATTRIBUTE_FILE_SIZE_EXCEPTION); + if (e instanceof FileSizeLimitExceededException) { + return (FileSizeLimitExceededException) e; + } else { + return null; + } + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java deleted file mode 100644 index 519e2f07c..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/FileUploadServletRequest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.servlet.ServletFileUpload; - -/** - *

- * Wraps an HTTP request and parses it for file uploads, without losing the - * request parameters. - *

- *

- * The request will have an attribute named by {@link #FILE_ITEM_MAP}. Either - * this attribute or the call to {@link #getFiles()} will produce a map that may - * be empty but is never null. The keys to the map are the field names for the - * file fields. Since a form may have multiple fields with the same name, each - * field name maps to a list of items. If a user does not provide a file to be - * uploaded in a given field, the length of the file will be 0. - *

- *

- * If the uploaded file(s) would be larger than the maxFileSize, - * {@link #parseRequest(HttpServletRequest, int)} does not throw an Exception. - * Instead, it records the exception in a request attribute named by - * {@link #FILE_UPLOAD_EXCEPTION}. This attribute can be accessed directly, or - * indirectly via the methods {@link #hasFileUploadException()} and - * {@link #getFileUploadException()}. If there is an exception, the file item - * map (see above) will still be non-null, but it will be empty. - *

- *

- * Most methods are declared here simply delegate to the wrapped request. - * Methods that have to do with parameters, files, or parsing exceptions, are - * handled differently for simple requests and multipart request, and are - * implemented in the sub-classes. - *

- */ -public abstract class FileUploadServletRequest extends HttpServletRequestWrapper { - public static final String FILE_ITEM_MAP = "file_item_map"; - public static final String FILE_UPLOAD_EXCEPTION = "file_upload_exception"; - - // ---------------------------------------------------------------------- - // The factory method - // ---------------------------------------------------------------------- - - /** - * Wrap this {@link HttpServletRequest} in an appropriate wrapper class. - */ - public static FileUploadServletRequest parseRequest( - HttpServletRequest request, int maxFileSize) throws IOException { - boolean isMultipart = ServletFileUpload.isMultipartContent(request); - if (isMultipart) { - return new MultipartHttpServletRequest(request, maxFileSize); - } else { - return new SimpleHttpServletRequestWrapper(request); - } - } - - // ---------------------------------------------------------------------- - // The constructor and the delegate. - // ---------------------------------------------------------------------- - - private final HttpServletRequest delegate; - - public FileUploadServletRequest(HttpServletRequest delegate) { - super(delegate); - this.delegate = delegate; - } - - protected HttpServletRequest getDelegate() { - return this.delegate; - } - - // ---------------------------------------------------------------------- - // New functionality to be implemented by the subclasses. - // ---------------------------------------------------------------------- - - /** Was this a multipart HTTP request? */ - public abstract boolean isMultipart(); - - /** - * Get the map of file items, by name. - */ - public abstract Map> getFiles(); - - /** - * Find a non-empty file item with this name. - * - * @return the first such file item, or null if no matching, - * non-empty items were found. - */ - public abstract FileItem getFileItem(String string); - - /** - * Was there an exception when uploading the file items? - */ - public abstract boolean hasFileUploadException(); - - /** - * Get the exception that occurred when uploading the file items. If no such - * exception, return null. - */ - public abstract FileUploadException getFileUploadException(); - -} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java deleted file mode 100644 index 56cf480fc..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/MultipartHttpServletRequest.java +++ /dev/null @@ -1,261 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * A wrapper for a servlet request that holds multipart content. Parsing the - * request will consume the parameters, so we need to hold them here to answer - * any parameter-related requests. File-related information will also be held - * here, to answer file-related requests. - */ -class MultipartHttpServletRequest extends FileUploadServletRequest { - private static final Log log = LogFactory - .getLog(MultipartHttpServletRequest.class); - - private static final String[] EMPTY_ARRAY = new String[0]; - - private final Map> parameters; - private final Map> files; - private FileUploadException fileUploadException; - - /** - * Parse the multipart request. Store the info about the request parameters - * and the uploaded files. - */ - public MultipartHttpServletRequest(HttpServletRequest request, - int maxFileSize) throws IOException { - super(request); - - Map> parameters = new HashMap>(); - Map> files = new HashMap>(); - - File tempDir = figureTemporaryDirectory(request); - ServletFileUpload upload = createUploadHandler(maxFileSize, tempDir); - - parseQueryString(request.getQueryString(), parameters); - - try { - List items = parseRequestIntoFileItems(request, upload); - - for (FileItem item : items) { - // Process a regular form field - if (item.isFormField()) { - addToParameters(parameters, item.getFieldName(), item - .getString("UTF-8")); - log.debug("Form field (parameter) " + item.getFieldName() - + "=" + item.getString()); - } else { - addToFileItems(files, item); - log.debug("File " + item.getFieldName() + ": " - + item.getName()); - } - } - } catch (FileUploadException e) { - fileUploadException = e; - request.setAttribute( - FileUploadServletRequest.FILE_UPLOAD_EXCEPTION, e); - } - - this.parameters = Collections.unmodifiableMap(parameters); - log.debug("Parameters are: " + this.parameters); - this.files = Collections.unmodifiableMap(files); - log.debug("Files are: " + this.files); - request.setAttribute(FILE_ITEM_MAP, this.files); - } - - /** - * Pull any parameters out of the URL. - */ - private void parseQueryString(String queryString, - Map> parameters) { - log.debug("Query string is : '" + queryString + "'"); - if (queryString != null) { - String[] pieces = queryString.split("&"); - - for (String piece : pieces) { - int equalsHere = piece.indexOf('='); - if (piece.trim().isEmpty()) { - // Ignore an empty piece. - } else if (equalsHere <= 0) { - // A parameter without a value. - addToParameters(parameters, decode(piece), ""); - } else { - // A parameter with a value. - String key = piece.substring(0, equalsHere); - String value = piece.substring(equalsHere + 1); - addToParameters(parameters, decode(key), decode(value)); - } - } - } - log.debug("Parameters from query string are: " + parameters); - } - - /** - * Remove any special URL-style encoding. - */ - private String decode(String encoded) { - try { - return URLDecoder.decode(encoded, "UTF-8"); - } catch (UnsupportedEncodingException e) { - log.error(e, e); - return encoded; - } - } - - /** - * Find the temporary storage directory for this webapp. - */ - private File figureTemporaryDirectory(HttpServletRequest request) { - return (File) request.getSession().getServletContext().getAttribute( - "javax.servlet.context.tempdir"); - } - - /** - * Create an upload handler that will throw an exception if the file is too - * large. - */ - private ServletFileUpload createUploadHandler(int maxFileSize, File tempDir) { - DiskFileItemFactory factory = new DiskFileItemFactory(); - factory.setSizeThreshold(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD); - factory.setRepository(tempDir); - - ServletFileUpload upload = new ServletFileUpload(factory); - upload.setSizeMax(maxFileSize); - - return upload; - } - - /** Either create a new List for the value, or add to an existing List. */ - private void addToParameters(Map> map, String name, - String value) { - if (!map.containsKey(name)) { - map.put(name, new ArrayList()); - } - map.get(name).add(value); - } - - /** Either create a new List for the file, or add to an existing List. */ - private void addToFileItems(Map> map, FileItem file) { - String name = file.getFieldName(); - if (!map.containsKey(name)) { - map.put(name, new ArrayList()); - } - map.get(name).add(file); - } - - /** Minimize the code that uses the unchecked cast. */ - @SuppressWarnings("unchecked") - private List parseRequestIntoFileItems(HttpServletRequest req, - ServletFileUpload upload) throws FileUploadException { - return upload.parseRequest(req); - } - - // ---------------------------------------------------------------------- - // This is a multipart request, so make the file info available. If there - // was an exception during parsing, make that available too. - // ---------------------------------------------------------------------- - - @Override - public boolean isMultipart() { - return true; - } - - @Override - public Map> getFiles() { - return files; - } - - /** - * {@inheritDoc} - *

- * There may be more than one file item with the given name. If the first - * one is empty (size is zero), keep looking for a non-empty one. - *

- */ - @Override - public FileItem getFileItem(String name) { - List items = files.get(name); - if (items == null) { - return null; - } - - for (FileItem item : items) { - if (item.getSize() > 0L) { - return item; - } - } - - return null; - } - - @Override - public FileUploadException getFileUploadException() { - return fileUploadException; - } - - @Override - public boolean hasFileUploadException() { - return fileUploadException != null; - } - - // ---------------------------------------------------------------------- - // Parameter-related methods won't find anything on the delegate request, - // since parsing consumed the parameters. So we need to look to the parsed - // info for the answers. - // ---------------------------------------------------------------------- - - @Override - public String getParameter(String name) { - if (parameters.containsKey(name)) { - return parameters.get(name).get(0); - } else { - return null; - } - } - - @Override - public Enumeration getParameterNames() { - return Collections.enumeration(parameters.keySet()); - } - - @Override - public String[] getParameterValues(String name) { - if (parameters.containsKey(name)) { - return parameters.get(name).toArray(EMPTY_ARRAY); - } else { - return null; - } - } - - @Override - public Map getParameterMap() { - Map result = new HashMap(); - for (Entry> entry : parameters.entrySet()) { - result.put(entry.getKey(), entry.getValue().toArray(EMPTY_ARRAY)); - } - log.debug("resulting parameter map: " + result); - return result; - } - -} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/SimpleHttpServletRequestWrapper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/SimpleHttpServletRequestWrapper.java deleted file mode 100644 index 246377048..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/uploadrequest/SimpleHttpServletRequestWrapper.java +++ /dev/null @@ -1,81 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest; - -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUploadException; - -/** - * A wrapper for a servlet request that does not hold multipart content. Pass - * all parameter-related requests to the delegate, and give simple answers to - * all file-related requests. - */ -class SimpleHttpServletRequestWrapper extends FileUploadServletRequest { - - SimpleHttpServletRequestWrapper(HttpServletRequest request) { - super(request); - request.setAttribute(FILE_ITEM_MAP, Collections.EMPTY_MAP); - } - - // ---------------------------------------------------------------------- - // Not a multipart request, so there are no files or upload exceptions. - // ---------------------------------------------------------------------- - - @Override - public boolean isMultipart() { - return false; - } - - @Override - public Map> getFiles() { - return Collections.emptyMap(); - } - - @Override - public FileItem getFileItem(String string) { - return null; - } - - @Override - public FileUploadException getFileUploadException() { - return null; - } - - @Override - public boolean hasFileUploadException() { - return false; - } - - // ---------------------------------------------------------------------- - // Since this is not a multipart request, the parameter methods can be - // delegated. - // ---------------------------------------------------------------------- - - @Override - public String getParameter(String name) { - return getDelegate().getParameter(name); - } - - @Override - public Map getParameterMap() { - return getDelegate().getParameterMap(); - } - - @Override - public Enumeration getParameterNames() { - return getDelegate().getParameterNames(); - } - - @Override - public String[] getParameterValues(String name) { - return getDelegate().getParameterValues(name); - } - -} From c42d9af70fe471baaf77ce6483af9986ea358cf0 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 20 Dec 2013 17:11:16 -0500 Subject: [PATCH 06/31] VIVO-617 Adjust the client code to use the new file upload tools. --- .../FedoraDatastreamController.java | 19 ++- .../freemarker/ImageUploadController.java | 109 ++++++------------ .../freemarker/ImageUploadHelper.java | 19 +-- .../jena/JenaCsv2RdfController.java | 24 ++-- .../controller/jena/JenaXMLFileUpload.java | 26 +++-- .../controller/jena/RDFUploadController.java | 39 ++++--- .../controller/SearchServiceController.java | 9 +- .../search/controller/UpdateUrisInIndex.java | 11 +- 8 files changed, 116 insertions(+), 140 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java index 93def6de3..9ae6b7b86 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java @@ -45,7 +45,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; import fedora.client.FedoraClient; import fedora.common.Constants; import fedora.server.management.FedoraAPIM; @@ -220,12 +219,22 @@ public class FedoraDatastreamController extends VitroHttpServlet implements Cons } @Override + public long maximumMultipartFileSize() { + return maxFileSize; + } + + @Override + public boolean stashFileSizeException() { + return true; + } + + @Override public void doPost(HttpServletRequest rawRequest, HttpServletResponse res) throws ServletException, IOException { - try{ - FileUploadServletRequest req = FileUploadServletRequest.parseRequest(rawRequest, maxFileSize); - if (req.hasFileUploadException()) { - throw new FdcException("Size limit exceeded: " + req.getFileUploadException().getLocalizedMessage()); + try{ + VitroRequest req = new VitroRequest(rawRequest); + if (req.hasFileSizeException()) { + throw new FdcException("Size limit exceeded: " + req.getFileSizeException().getLocalizedMessage()); } if (!req.isMultipart()) { throw new FdcException("Must POST a multipart encoded request"); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadController.java index 537b4573c..d01d163e9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadController.java @@ -2,12 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Map; -import java.util.Map.Entry; - -import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServletRequest; @@ -36,7 +30,6 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage; import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil; @@ -52,6 +45,7 @@ public class ImageUploadController extends FreemarkerHttpServlet { private static final String ERROR_CODE_UNRECOGNIZED_URI = "imageUpload.errorUnrecognizedURI"; private static final String ERROR_CODE_NO_URI = "imageUpload.errorNoURI"; + private static final String ERROR_CODE_FILE_TOO_BIG = "imageUpload.errorFileTooBig"; /** Limit file size to 6 megabytes. */ public static final int MAXIMUM_FILE_SIZE = 6 * 1024 * 1024; @@ -136,6 +130,22 @@ public class ImageUploadController extends FreemarkerHttpServlet { + FileStorage.class.getName() + "'"); } } + + /** + * How large an image file will we accept? + */ + @Override + public long maximumMultipartFileSize() { + return MAXIMUM_FILE_SIZE; + } + + /** + * What will we do if there is a problem parsing the request? + */ + @Override + public boolean stashFileSizeException() { + return true; + } /** * The required action depends on what we are trying to do. @@ -171,52 +181,17 @@ public class ImageUploadController extends FreemarkerHttpServlet { } } - /** - *

- * Parse the multi-part request, process the request, and produce the - * output. - *

- *

- * If the request was a multi-part file upload, it will parse to a - * normal-looking request with a "file_item_map" attribute. - *

- *

- * The processing will produce a {@link ResponseValues} object, which - * represents either a request for a FreeMarker template or a forwarding - * operation. - *

    - *
  • If a FreeMarker template, we emulate the actions that - * FreeMarkerHttpServlet would have taken to produce the output.
  • - *
  • If a forwarding operation, we create a {@link RequestDispatcher} to - * do the forwarding.
  • - *
- *

- */ - - @Override - protected ResponseValues processRequest(VitroRequest vreq) { - try { - // Parse the multi-part request. - FileUploadServletRequest.parseRequest(vreq, MAXIMUM_FILE_SIZE); - if (log.isTraceEnabled()) { - dumpRequestDetails(vreq); - } - - return buildTheResponse(vreq); - } catch (Exception e) { - // log.error("Could not produce response page", e); - return new ExceptionResponseValues(e); - } - } - /** * Handle the different actions. If not specified, the default action is to * show the intro screen. */ - private ResponseValues buildTheResponse(VitroRequest vreq) { - String action = vreq.getParameter(PARAMETER_ACTION); - + @Override + protected ResponseValues processRequest(VitroRequest vreq) { try { + checkForFileTooBigException(vreq); + + String action = vreq.getParameter(PARAMETER_ACTION); + Individual entity = validateEntityUri(vreq); if (ACTION_UPLOAD.equals(action)) { return doUploadImage(vreq, entity); @@ -240,6 +215,19 @@ public class ImageUploadController extends FreemarkerHttpServlet { } } + /** + * If our exception handler caught a "file too big" exception, we need to + * deal with it before anything else, since we can't trust the other + * parameters. + */ + private void checkForFileTooBigException(VitroRequest vreq) + throws UserMistakeException { + if (vreq.hasFileSizeException()) { + int limit = MAXIMUM_FILE_SIZE / (1024 * 1024); + throw new UserMistakeException(ERROR_CODE_FILE_TOO_BIG, limit); + } + } + /** * We are just starting the upload process. Record where we came from, so if * they hit "cancel" we know where to send them. If we have problems, just @@ -626,31 +614,6 @@ public class ImageUploadController extends FreemarkerHttpServlet { } - /** - * For debugging, dump all sorts of information about the request. - * - * WARNING: if "req" represents a Multi-part request which has not yet been - * parsed, then reading these parameters will consume them. - */ - @SuppressWarnings("unchecked") - private void dumpRequestDetails(HttpServletRequest req) { - log.trace("Request is " + req.getClass().getName()); - - Map parms = req.getParameterMap(); - for (Entry entry : parms.entrySet()) { - log.trace("Parameter '" + entry.getKey() + "'=" - + Arrays.deepToString(entry.getValue())); - } - - Enumeration attrs = req.getAttributeNames(); - while (attrs.hasMoreElements()) { - String key = attrs.nextElement(); - String valueString = String.valueOf(req.getAttribute(key)); - String valueOneLine = valueString.replace("\n", " | "); - log.trace("Attribute '" + key + "'=" + valueOneLine); - } - } - static class Dimensions { final int width; final int height; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java index 68d0d00f6..3528ecc35 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java @@ -2,12 +2,9 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; -import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.MAXIMUM_FILE_SIZE; import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.PARAMETER_UPLOADED_FILE; import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT; import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH; -import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_ITEM_MAP; -import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_UPLOAD_EXCEPTION; import java.io.FileNotFoundException; import java.io.IOException; @@ -21,7 +18,6 @@ import javax.media.jai.JAI; import javax.media.jai.RenderedOp; import javax.media.jai.util.ImagingListener; import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.FilenameUtils; @@ -41,7 +37,6 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper; import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException; import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; /** * Handle the mechanics of validating, storing, and deleting file images. @@ -55,7 +50,6 @@ public class ImageUploadHelper { private static final String ERROR_CODE_NO_IMAGE_TO_CROP = "imageUpload.errorNoImageForCropping"; private static final String ERROR_CODE_IMAGE_TOO_SMALL = "imageUpload.errorImageTooSmall"; private static final String ERROR_CODE_UNKNOWN = "imageUpload.errorUnknown"; - private static final String ERROR_CODE_FILE_TOO_BIG = "imageUpload.errorFileTooBig"; private static final String ERROR_CODE_UNRECOGNIZED_FILE_TYPE = "imageUpload.errorUnrecognizedFileType"; private static final String ERROR_CODE_NO_PHOTO_SELECTED = "imageUpload.errorNoPhotoSelected"; private static final String ERROR_CODE_BAD_MULTIPART_REQUEST = "imageUpload.errorBadMultipartRequest"; @@ -133,20 +127,13 @@ public class ImageUploadHelper { * if there is no file, if it is empty, or if it is not an image * file. */ - @SuppressWarnings("unchecked") - FileItem validateImageFromRequest(HttpServletRequest request) + FileItem validateImageFromRequest(VitroRequest vreq) throws UserMistakeException { - Object exception = request.getAttribute(FILE_UPLOAD_EXCEPTION); - if (exception != null) { - int limit = MAXIMUM_FILE_SIZE / (1024 * 1024); - throw new UserMistakeException(ERROR_CODE_FILE_TOO_BIG, limit); - } - - Map> map = (Map>) request - .getAttribute(FILE_ITEM_MAP); + Map> map = vreq.getFiles(); if (map == null) { throw new IllegalStateException(ERROR_CODE_BAD_MULTIPART_REQUEST); } + List list = map.get(PARAMETER_UPLOADED_FILE); if ((list == null) || list.isEmpty()) { throw new UserMistakeException(ERROR_CODE_FORM_FIELD_MISSING, diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java index c310de156..877bb05c0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java @@ -25,7 +25,6 @@ import com.hp.hpl.jena.rdf.model.ModelMaker; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.controller.Controllers; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; import edu.cornell.mannlib.vitro.webapp.utils.Csv2Rdf; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils; @@ -37,6 +36,17 @@ public class JenaCsv2RdfController extends JenaIngestController { private static final String CSV2RDF_SELECT_URI_JSP = "/jenaIngest/csv2rdfSelectUri.jsp"; private static int maxFileSizeInBytes = 1024 * 1024 * 2000; //2000mb + + @Override + public long maximumMultipartFileSize() { + return maxFileSizeInBytes; + } + + @Override + public boolean stashFileSizeException() { + return true; + } + @Override public void doPost(HttpServletRequest rawRequest, HttpServletResponse response) throws ServletException, IOException { @@ -45,15 +55,13 @@ public class JenaCsv2RdfController extends JenaIngestController { return; } - FileUploadServletRequest req = FileUploadServletRequest.parseRequest(rawRequest, - maxFileSizeInBytes); - if (req.hasFileUploadException()) { - forwardToFileUploadError(req.getFileUploadException().getLocalizedMessage(), req, response); + VitroRequest request = new VitroRequest(rawRequest); + if (request.hasFileSizeException()) { + forwardToFileUploadError(request.getFileSizeException().getLocalizedMessage(), request, response); return; } - VitroRequest request = new VitroRequest(req); - Map> fileStreams = req.getFiles(); + Map> fileStreams = request.getFiles(); FileItem fileStream = fileStreams.get("filePath").get(0); String filePath = fileStreams.get("filePath").get(0).getName(); @@ -70,7 +78,7 @@ public class JenaCsv2RdfController extends JenaIngestController { csv2rdfResult = doExecuteCsv2Rdf( request, fileStream, filePath); }catch(Exception ex){ - forwardToFileUploadError(ex.getMessage(),req,response); + forwardToFileUploadError(ex.getMessage(),request,response); return; } ModelMaker maker = getVitroJenaModelMaker(request); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaXMLFileUpload.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaXMLFileUpload.java index f9ea1335b..39e862325 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaXMLFileUpload.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaXMLFileUpload.java @@ -36,7 +36,6 @@ import com.hp.hpl.jena.shared.Lock; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.controller.Controllers; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; public class JenaXMLFileUpload extends JenaIngestController { Log log = LogFactory.getLog(JenaXMLFileUpload.class); @@ -76,6 +75,16 @@ public class JenaXMLFileUpload extends JenaIngestController { } } + @Override + public long maximumMultipartFileSize() { + return maxFileSize; + } + + @Override + public boolean stashFileSizeException() { + return true; + } + /** * Each file will be converted to RDF/XML and loaded to the target model. * If any of the files fail, no data will be loaded. @@ -86,14 +95,14 @@ public class JenaXMLFileUpload extends JenaIngestController { * */ @Override - public void doPost(HttpServletRequest rawRequest, HttpServletResponse resp) + public void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { - FileUploadServletRequest request = FileUploadServletRequest.parseRequest(rawRequest, maxFileSize); - if (request.hasFileUploadException()) { + VitroRequest vreq = new VitroRequest(request); + if (vreq.hasFileSizeException()) { throw new ServletException("Size limit exceeded: " - + request.getFileUploadException().getLocalizedMessage()); + + vreq.getFileSizeException().getLocalizedMessage()); } - if (request.isMultipart()) { + if (vreq.isMultipart()) { log.debug("multipart content detected"); } else { // TODO: forward to error message @@ -105,7 +114,6 @@ public class JenaXMLFileUpload extends JenaIngestController { return; } - VitroRequest vreq = new VitroRequest(request); ModelMaker modelMaker = getVitroJenaModelMaker(vreq); String targetModel = request.getParameter("targetModel"); if (targetModel == null) { @@ -117,7 +125,7 @@ public class JenaXMLFileUpload extends JenaIngestController { throw new ServletException("targetModel '" + targetModel + "' was not found."); request.setAttribute("targetModel", targetModel); - List filesToLoad = saveFiles( request.getFiles() ); + List filesToLoad = saveFiles( vreq.getFiles() ); List rdfxmlToLoad = convertFiles( filesToLoad); List modelsToLoad = loadRdfXml( rdfxmlToLoad ); @@ -136,7 +144,7 @@ public class JenaXMLFileUpload extends JenaIngestController { request.setAttribute("title","Uploaded files and converted to RDF"); request.setAttribute("bodyJsp","/jenaIngest/xmlFileUploadSuccess.jsp"); - request.setAttribute("fileItems",request.getFiles()); + request.setAttribute("fileItems",vreq.getFiles()); RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); request.setAttribute("css", ""); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java index a196d8d28..bcb2eb635 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java @@ -42,7 +42,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector; import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceGraph; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; -import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; @@ -56,31 +55,39 @@ public class RDFUploadController extends JenaIngestController { private static FileItem fileStream = null; private static final String LOAD_RDF_DATA_JSP="/jenaIngest/loadRDFData.jsp"; - public void doPost(HttpServletRequest rawRequest, + @Override + public long maximumMultipartFileSize() { + return maxFileSizeInBytes; + } + + @Override + public boolean stashFileSizeException() { + return true; + } + + public void doPost(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { - if (!isAuthorizedToDisplayPage(rawRequest, response, + if (!isAuthorizedToDisplayPage(req, response, SimplePermission.USE_ADVANCED_DATA_TOOLS_PAGES.ACTIONS)) { return; } - FileUploadServletRequest req = FileUploadServletRequest.parseRequest( - rawRequest, maxFileSizeInBytes); - if (req.hasFileUploadException()) { + VitroRequest request = new VitroRequest(req); + if (request.hasFileSizeException()) { forwardToFileUploadError( - req.getFileUploadException().getLocalizedMessage(), + request.getFileSizeException().getLocalizedMessage(), req, response); return; } - Map> fileStreams = req.getFiles(); + Map> fileStreams = request.getFiles(); - VitroRequest request = new VitroRequest(req); LoginStatusBean loginBean = LoginStatusBean.getBean(request); try { String modelName = req.getParameter("modelName"); if(modelName!=null){ - loadRDF(req,request,response); + loadRDF(request,response); return; } } catch (Exception e) { @@ -234,15 +241,13 @@ public class RDFUploadController extends JenaIngestController { } } - public void loadRDF(FileUploadServletRequest req, - VitroRequest request, - HttpServletResponse response) - throws ServletException, IOException { - Map> fileStreams = req.getFiles(); + public void loadRDF(VitroRequest request, HttpServletResponse response) + throws ServletException { + Map> fileStreams = request.getFiles(); String filePath = fileStreams.get("filePath").get(0).getName(); fileStream = fileStreams.get("filePath").get(0); - String modelName = req.getParameter("modelName"); - String docLoc = req.getParameter("docLoc"); + String modelName = request.getParameter("modelName"); + String docLoc = request.getParameter("docLoc"); String languageStr = request.getParameter("language"); ModelMaker maker = getVitroJenaModelMaker(request); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java index 88175db88..490fff4ef 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/SearchServiceController.java @@ -23,7 +23,6 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServ 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.filestorage.uploadrequest.FileUploadServletRequest; import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder; /** @@ -35,7 +34,10 @@ public class SearchServiceController extends FreemarkerHttpServlet { .getLog(SearchServiceController.class); /** Limit file size to 1 Gigabyte. */ - public static final int MAXIMUM_FILE_SIZE = 1024 * 1024 * 1024; + @Override + public long maximumMultipartFileSize() { + return 1024 * 1024 * 1024; + } /** * Handle the different actions. If not specified, the default action is to @@ -44,9 +46,6 @@ public class SearchServiceController extends FreemarkerHttpServlet { @Override protected ResponseValues processRequest(VitroRequest req) { try { - req = new VitroRequest(FileUploadServletRequest.parseRequest(req, - MAXIMUM_FILE_SIZE)); - // Check the authorization here, because we don't want to redirect // to the login page if they are not authorized. (The file upload // would be lost. diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java index 5c3e1636f..f723613ac 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java @@ -2,8 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.search.controller; -import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_ITEM_MAP; - import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -21,6 +19,7 @@ import org.apache.commons.lang.StringUtils; 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.search.indexing.IndexBuilder; /** @@ -41,9 +40,7 @@ public class UpdateUrisInIndex { */ protected int doUpdateUris(HttpServletRequest req, IndexBuilder builder) throws ServletException, IOException { - @SuppressWarnings("unchecked") - Map> map = (Map>) req - .getAttribute(FILE_ITEM_MAP); + Map> map = new VitroRequest(req).getFiles(); if (map == null) { throw new ServletException("Expected Multipart Content"); } @@ -54,13 +51,13 @@ public class UpdateUrisInIndex { for (String name : map.keySet()) { for (FileItem item : map.get(name)) { log.debug("Found " + item.getSize() + " byte file for '" + name + "'"); - uriCount += processFileItem(builder, name, item, enc); + uriCount += processFileItem(builder, item, enc); } } return uriCount; } - private int processFileItem(IndexBuilder builder, String name, + private int processFileItem(IndexBuilder builder, FileItem item, Charset enc) throws IOException { int count = 0; Reader reader = new InputStreamReader(item.getInputStream(), enc.name()); From ff9aecafa41c2edb34fbd96d103a3654ae6812a9 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 20 Dec 2013 17:12:48 -0500 Subject: [PATCH 07/31] line-end characters --- .../utilities/testing/VitroTestRunner.java | 288 +++++++++--------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java b/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java index 76664d4e3..0c4f5ae2c 100644 --- a/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java +++ b/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/testing/VitroTestRunner.java @@ -1,7 +1,7 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ -package edu.cornell.mannlib.vitro.utilities.testing; - +package edu.cornell.mannlib.vitro.utilities.testing; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -9,145 +9,145 @@ import java.util.SortedSet; import java.util.TreeSet; import org.junit.runner.JUnitCore; - -/** - * A Java application that will run the Vitro unit tests. It searches for unit - * tests in the supplied source directory (any file whose name is *Test.Java). - * It runs the tests with a variety of reporting detail, depending on the level - * selected. If the level selector is absent or unrecognized, the medium level - * is used. - * - * @author jeb228 - */ -public class VitroTestRunner { - public enum ReportLevel { - /** Report only the one-line summary. */ - BRIEF, - /** Report times and statistics for each test class. */ - MORE, - /** Report times and statistics for each test method. */ - FULL - } - - private final List> classes; - private final VitroTestRunListener listener; - - /** - * Locate the test classes. Initialize the test listener. - */ - public VitroTestRunner(File sourceRootDir, ReportLevel reportLevel) { - List classNames = getListOfTestClassNames(sourceRootDir); - this.classes = getTestClasses(classNames); - this.listener = new VitroTestRunListener(reportLevel); - } - - /** - * Start a recursive search through the source directory. - */ - private List getListOfTestClassNames(File sourceRootDir) { - SortedSet names = new TreeSet(); - searchForTestClasses(names, "", sourceRootDir); - return new ArrayList(names); - } - - /** - * Recursively search the directory for files in the form "*Test.java". - * Ignore any files or directories whose names start with a "." - */ - private void searchForTestClasses(SortedSet names, String prefix, - File directory) { - for (File file : directory.listFiles()) { - String filename = file.getName(); - if (filename.startsWith(".")) { - // ignore .svn, etc. - } else if (file.isDirectory()) { - searchForTestClasses(names, prefix + filename + ".", file); - } else if (filename.endsWith("Test.java")) { - String classname = filename.substring(0, filename.length() - 5); - names.add(prefix + classname); - } - } - } - - /** - * Instantiate a class for each test class name. - */ - private List> getTestClasses(List classNames) { - List> classes = new ArrayList>(); - for (String classname : classNames) { - try { - classes.add(Class.forName(classname)); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Can't load test class: " - + classname, e); - } - } - return classes; - } - - /** - * We've located all of the test clases. Now run them. - */ - private void run() { - JUnitCore junitCore = new JUnitCore(); - junitCore.addListener(this.listener); - junitCore.run(this.classes.toArray(new Class[0])); - } - - /** - * Did any of the tests fail? - */ - private boolean didEverythingPass() { - return this.listener.didEverythingPass(); - } - - /** - *

- * You must provide a path to the source directory of the test classes. - *

- *

- * You may also provide a reporting level of "BRIEF", "MORE", or "FULL". If - * no level is provided, or if it is not recognized, "BRIEF" is used. - *

- */ - public static void main(String[] args) { - if ((args.length < 1) || (args.length > 2)) { - usage("Wrong number of arguments: expecting 1 or 2, but found " - + args.length + "."); - } - File sourceRootDir = new File(args[0]); - - if (!sourceRootDir.exists()) { - usage(sourceRootDir + " does not exist."); - } - if (!sourceRootDir.isDirectory()) { - usage(sourceRootDir + " is not a directory."); - } - - ReportLevel reportLevel = ReportLevel.MORE; - if (args.length == 2) { - for (ReportLevel level : ReportLevel.values()) { - if (level.toString().equalsIgnoreCase(args[1])) { - reportLevel = level; - } - } - } - - VitroTestRunner runner = new VitroTestRunner(sourceRootDir, reportLevel); - runner.run(); - - if (!runner.didEverythingPass()) { - System.exit(1); - } - } - - /** - * Tell them how it should have been done. - */ - private static void usage(String message) { - System.out.println(message); - System.out.println("usage: " + VitroTestRunner.class.getSimpleName() - + " sourceRootDirectory [ BRIEF | MORE | FULL ]"); - System.exit(1); - } -} + +/** + * A Java application that will run the Vitro unit tests. It searches for unit + * tests in the supplied source directory (any file whose name is *Test.Java). + * It runs the tests with a variety of reporting detail, depending on the level + * selected. If the level selector is absent or unrecognized, the medium level + * is used. + * + * @author jeb228 + */ +public class VitroTestRunner { + public enum ReportLevel { + /** Report only the one-line summary. */ + BRIEF, + /** Report times and statistics for each test class. */ + MORE, + /** Report times and statistics for each test method. */ + FULL + } + + private final List> classes; + private final VitroTestRunListener listener; + + /** + * Locate the test classes. Initialize the test listener. + */ + public VitroTestRunner(File sourceRootDir, ReportLevel reportLevel) { + List classNames = getListOfTestClassNames(sourceRootDir); + this.classes = getTestClasses(classNames); + this.listener = new VitroTestRunListener(reportLevel); + } + + /** + * Start a recursive search through the source directory. + */ + private List getListOfTestClassNames(File sourceRootDir) { + SortedSet names = new TreeSet(); + searchForTestClasses(names, "", sourceRootDir); + return new ArrayList(names); + } + + /** + * Recursively search the directory for files in the form "*Test.java". + * Ignore any files or directories whose names start with a "." + */ + private void searchForTestClasses(SortedSet names, String prefix, + File directory) { + for (File file : directory.listFiles()) { + String filename = file.getName(); + if (filename.startsWith(".")) { + // ignore .svn, etc. + } else if (file.isDirectory()) { + searchForTestClasses(names, prefix + filename + ".", file); + } else if (filename.endsWith("Test.java")) { + String classname = filename.substring(0, filename.length() - 5); + names.add(prefix + classname); + } + } + } + + /** + * Instantiate a class for each test class name. + */ + private List> getTestClasses(List classNames) { + List> classes = new ArrayList>(); + for (String classname : classNames) { + try { + classes.add(Class.forName(classname)); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Can't load test class: " + + classname, e); + } + } + return classes; + } + + /** + * We've located all of the test clases. Now run them. + */ + private void run() { + JUnitCore junitCore = new JUnitCore(); + junitCore.addListener(this.listener); + junitCore.run(this.classes.toArray(new Class[0])); + } + + /** + * Did any of the tests fail? + */ + private boolean didEverythingPass() { + return this.listener.didEverythingPass(); + } + + /** + *

+ * You must provide a path to the source directory of the test classes. + *

+ *

+ * You may also provide a reporting level of "BRIEF", "MORE", or "FULL". If + * no level is provided, or if it is not recognized, "BRIEF" is used. + *

+ */ + public static void main(String[] args) { + if ((args.length < 1) || (args.length > 2)) { + usage("Wrong number of arguments: expecting 1 or 2, but found " + + args.length + "."); + } + File sourceRootDir = new File(args[0]); + + if (!sourceRootDir.exists()) { + usage(sourceRootDir + " does not exist."); + } + if (!sourceRootDir.isDirectory()) { + usage(sourceRootDir + " is not a directory."); + } + + ReportLevel reportLevel = ReportLevel.MORE; + if (args.length == 2) { + for (ReportLevel level : ReportLevel.values()) { + if (level.toString().equalsIgnoreCase(args[1])) { + reportLevel = level; + } + } + } + + VitroTestRunner runner = new VitroTestRunner(sourceRootDir, reportLevel); + runner.run(); + + if (!runner.didEverythingPass()) { + System.exit(1); + } + } + + /** + * Tell them how it should have been done. + */ + private static void usage(String message) { + System.out.println(message); + System.out.println("usage: " + VitroTestRunner.class.getSimpleName() + + " sourceRootDirectory [ BRIEF | MORE | FULL ]"); + System.exit(1); + } +} From 004427855b0d6d38e346efa776623b500a8bf87a Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 3 Jan 2014 12:41:37 -0500 Subject: [PATCH 08/31] VIVO-224 Get rid of MANAGE_PORTALS permission It was only used in editing the application bean, and was equivalent to EDIT_SITE_INFORMATION. --- webapp/rdf/auth/everytime/permission_config.n3 | 2 -- webapp/rdf/display/everytime/permissions.n3 | 5 ----- .../vitro/webapp/auth/permissions/SimplePermission.java | 2 -- webapp/web/templates/edit/specific/applicationBean_retry.jsp | 2 +- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index 3af62fb71..deb05153f 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -35,7 +35,6 @@ auth:ADMIN # permissions for CURATOR and above. auth:hasPermission simplePermission:EditOntology ; auth:hasPermission simplePermission:EditSiteInformation ; - auth:hasPermission simplePermission:ManagePortals ; auth:hasPermission simplePermission:ManageTabs ; auth:hasPermission simplePermission:SeeVerbosePropertyInformation ; auth:hasPermission simplePermission:UseMiscellaneousCuratorPages ; @@ -74,7 +73,6 @@ auth:CURATOR # permissions for CURATOR and above. auth:hasPermission simplePermission:EditOntology ; auth:hasPermission simplePermission:EditSiteInformation ; - auth:hasPermission simplePermission:ManagePortals ; auth:hasPermission simplePermission:ManageTabs ; auth:hasPermission simplePermission:SeeVerbosePropertyInformation ; auth:hasPermission simplePermission:UseMiscellaneousCuratorPages ; diff --git a/webapp/rdf/display/everytime/permissions.n3 b/webapp/rdf/display/everytime/permissions.n3 index d622a8aa7..327d84b02 100644 --- a/webapp/rdf/display/everytime/permissions.n3 +++ b/webapp/rdf/display/everytime/permissions.n3 @@ -53,11 +53,6 @@ action:ManageOwnProxies rdfs:label "MANAGE_OWN_PROXIES" . -action:ManagePortals - a display:RequiredAction ; - rdfs:label "MANAGE_PORTALS" . - - action:ManageProxies a display:RequiredAction ; rdfs:label "MANAGE_PROXIES" . diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index 539356e17..e29767cf3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -46,8 +46,6 @@ public class SimplePermission extends Permission { NAMESPACE + "ManageMenus"); public static final SimplePermission MANAGE_OWN_PROXIES = new SimplePermission( NAMESPACE + "ManageOwnProxies"); - public static final SimplePermission MANAGE_PORTALS = new SimplePermission( - NAMESPACE + "ManagePortals"); public static final SimplePermission MANAGE_PROXIES = new SimplePermission( NAMESPACE + "ManageProxies"); public static final SimplePermission MANAGE_SEARCH_INDEX = new SimplePermission( diff --git a/webapp/web/templates/edit/specific/applicationBean_retry.jsp b/webapp/web/templates/edit/specific/applicationBean_retry.jsp index 0262336fa..67d78edb6 100644 --- a/webapp/web/templates/edit/specific/applicationBean_retry.jsp +++ b/webapp/web/templates/edit/specific/applicationBean_retry.jsp @@ -7,7 +7,7 @@ <%@taglib prefix="vitro" uri="/WEB-INF/tlds/VitroUtils.tld" %> <%@page import="edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission" %> -<% request.setAttribute("requestedActions", SimplePermission.MANAGE_PORTALS.ACTION); %> +<% request.setAttribute("requestedActions", SimplePermission.EDIT_SITE_INFORMATION.ACTION); %> From 3c5909852556d308c773eb4707c76ecfbbf7e971 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 3 Jan 2014 12:44:54 -0500 Subject: [PATCH 09/31] VIVO-224 Get rid of MANAGE_TABS permission It was not used, probably since we no longer have tabs. --- webapp/rdf/auth/everytime/permission_config.n3 | 2 -- webapp/rdf/display/everytime/permissions.n3 | 5 ----- .../vitro/webapp/auth/permissions/SimplePermission.java | 2 -- 3 files changed, 9 deletions(-) diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index deb05153f..ce304db4b 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -35,7 +35,6 @@ auth:ADMIN # permissions for CURATOR and above. auth:hasPermission simplePermission:EditOntology ; auth:hasPermission simplePermission:EditSiteInformation ; - auth:hasPermission simplePermission:ManageTabs ; auth:hasPermission simplePermission:SeeVerbosePropertyInformation ; auth:hasPermission simplePermission:UseMiscellaneousCuratorPages ; auth:hasPermission simplePermission:PageViewableCurator ; @@ -73,7 +72,6 @@ auth:CURATOR # permissions for CURATOR and above. auth:hasPermission simplePermission:EditOntology ; auth:hasPermission simplePermission:EditSiteInformation ; - auth:hasPermission simplePermission:ManageTabs ; auth:hasPermission simplePermission:SeeVerbosePropertyInformation ; auth:hasPermission simplePermission:UseMiscellaneousCuratorPages ; auth:hasPermission simplePermission:PageViewableCurator ; diff --git a/webapp/rdf/display/everytime/permissions.n3 b/webapp/rdf/display/everytime/permissions.n3 index 327d84b02..d16cdf784 100644 --- a/webapp/rdf/display/everytime/permissions.n3 +++ b/webapp/rdf/display/everytime/permissions.n3 @@ -63,11 +63,6 @@ action:ManageSearchIndex rdfs:label "MANAGE_SEARCH_INDEX" . -action:ManageTabs - a display:RequiredAction ; - rdfs:label "MANAGE_TABS" . - - action:ManageUserAccounts a display:RequiredAction ; rdfs:label "MANAGE_USER_ACCOUNTS" . diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index e29767cf3..4572cd06f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -50,8 +50,6 @@ public class SimplePermission extends Permission { NAMESPACE + "ManageProxies"); public static final SimplePermission MANAGE_SEARCH_INDEX = new SimplePermission( NAMESPACE + "ManageSearchIndex"); - public static final SimplePermission MANAGE_TABS = new SimplePermission( - NAMESPACE + "ManageTabs"); public static final SimplePermission MANAGE_USER_ACCOUNTS = new SimplePermission( NAMESPACE + "ManageUserAccounts"); public static final SimplePermission QUERY_FULL_MODEL = new SimplePermission( From 03dfbfc2c5c5478f89574c95578273ad2c9e5aa7 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 3 Jan 2014 15:26:33 -0500 Subject: [PATCH 10/31] VIVO-224 Get rid of REBUILD_VCLASS_GROUP_CACHE. Since we build the cache from Solr, we no longer provide the option to rebuild it. --- webapp/config/log4j.properties | 1 - .../rdf/auth/everytime/permission_config.n3 | 1 - webapp/rdf/display/everytime/permissions.n3 | 5 ---- .../auth/permissions/SimplePermission.java | 2 -- .../freemarker/BaseSiteAdminController.java | 1 - .../freemarker/BrowseController.java | 26 +------------------ 6 files changed, 1 insertion(+), 35 deletions(-) diff --git a/webapp/config/log4j.properties b/webapp/config/log4j.properties index b5cf2bf83..fbb00c755 100644 --- a/webapp/config/log4j.properties +++ b/webapp/config/log4j.properties @@ -35,7 +35,6 @@ log4j.rootLogger=INFO, AllAppender # These classes are too chatty to display INFO messages. log4j.logger.edu.cornell.mannlib.vitro.webapp.startup.StartupStatus=WARN -log4j.logger.edu.cornell.mannlib.vitro.webapp.controller.freemarker.BrowseController=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.dao.jena.RDBGraphGenerator=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase=DEBUG diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index ce304db4b..2a84a6609 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -18,7 +18,6 @@ auth:ADMIN auth:hasPermission simplePermission:ManageProxies ; auth:hasPermission simplePermission:ManageSearchIndex ; auth:hasPermission simplePermission:ManageUserAccounts ; - auth:hasPermission simplePermission:RebuildVClassGroupCache ; auth:hasPermission simplePermission:RefreshVisualizationCache ; auth:hasPermission simplePermission:SeeConfiguration ; auth:hasPermission simplePermission:SeeStartupStatus ; diff --git a/webapp/rdf/display/everytime/permissions.n3 b/webapp/rdf/display/everytime/permissions.n3 index d16cdf784..6b22e4e13 100644 --- a/webapp/rdf/display/everytime/permissions.n3 +++ b/webapp/rdf/display/everytime/permissions.n3 @@ -78,11 +78,6 @@ action:QueryUserAccountsModel rdfs:label "QUERY_USER_ACCOUNTS_MODEL" . -action:RebuildVClassGroupCache - a display:RequiredAction ; - rdfs:label "REBUILD_VCLASS_GROUP_CACHE" . - - action:RefreshVisualizationCache a display:RequiredAction ; rdfs:label "REFRESH_VISUALIZATION_CACHE" . diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index 4572cd06f..600fbe95c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -56,8 +56,6 @@ public class SimplePermission extends Permission { NAMESPACE + "QueryFullModel"); public static final SimplePermission QUERY_USER_ACCOUNTS_MODEL = new SimplePermission( NAMESPACE + "QueryUserAccountsModel"); - public static final SimplePermission REBUILD_VCLASS_GROUP_CACHE = new SimplePermission( - NAMESPACE + "RebuildVClassGroupCache"); public static final SimplePermission REFRESH_VISUALIZATION_CACHE = new SimplePermission( NAMESPACE + "RefreshVisualizationCache"); public static final SimplePermission SEE_CONFIGURATION = new SimplePermission( diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java index 1a10c92a3..dfcc56ba1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java @@ -66,7 +66,6 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet { if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.USE_MISCELLANEOUS_ADMIN_PAGES.ACTIONS)) { urls.put("recomputeInferences", UrlBuilder.getUrl("/RecomputeInferences")); - urls.put("rebuildClassGroupCache", UrlBuilder.getUrl("/browse?clearcache=1")); } if (PolicyHelper.isAuthorizedForActions(vreq, IndexController.REQUIRED_ACTIONS)) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java index ff921ce64..4a8f7ea36 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BrowseController.java @@ -10,9 +10,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; -import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; -import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; @@ -33,29 +30,12 @@ public class BrowseController extends FreemarkerHttpServlet { return "Index of Contents"; } - - @Override - protected Actions requiredActions(VitroRequest vreq) { - if ( vreq.getParameter("clearcache") != null ) - return SimplePermission.REBUILD_VCLASS_GROUP_CACHE.ACTIONS; - else - return Actions.AUTHORIZED; - } - - @Override protected ResponseValues processRequest(VitroRequest vreq) { Map body = new HashMap(); String templateName = TEMPLATE_DEFAULT; - if ( vreq.getParameter("clearcache") != null ) { - //mainly for debugging - if( PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.REBUILD_VCLASS_GROUP_CACHE.ACTIONS) ){ - clearGroupCache(); - } - } - List groups = null; VClassGroupsForRequest vcgc = VClassGroupCache.getVClassGroups(vreq); groups =vcgc.getGroups(); @@ -67,8 +47,4 @@ public class BrowseController extends FreemarkerHttpServlet { return new TemplateResponseValues(templateName, body); } - - protected void clearGroupCache(){ - VClassGroupCache.getVClassGroupCache(getServletContext()).doSynchronousRebuild(); - } -} \ No newline at end of file +} From f6bd5804d5b88e08df733ab7925f7bf0600c4e70 Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 3 Jan 2014 15:40:52 -0500 Subject: [PATCH 11/31] VIVO-224 Change USE_MISCELLANEOUS_EDITOR_PAGES to USE_INDIVIDUAL_CONTROL_PANEL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s hardly miscellaneous if there’s only one page. --- webapp/rdf/auth/everytime/permission_config.n3 | 6 +++--- webapp/rdf/display/everytime/permissions.n3 | 4 ++-- .../vitro/webapp/auth/permissions/SimplePermission.java | 4 ++-- webapp/web/templates/edit/specific/ents_edit.jsp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index 2a84a6609..d01e0b602 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -43,7 +43,7 @@ auth:ADMIN auth:hasPermission simplePermission:SeeIndividualEditingPanel ; auth:hasPermission simplePermission:SeeRevisionInfo ; auth:hasPermission simplePermission:SeeSiteAdminPage ; - auth:hasPermission simplePermission:UseMiscellaneousEditorPages ; + auth:hasPermission simplePermission:UseIndividualControlPanel ; auth:hasPermission simplePermission:PageViewableEditor ; # permissions for ANY logged-in user. @@ -80,7 +80,7 @@ auth:CURATOR auth:hasPermission simplePermission:SeeIndividualEditingPanel ; auth:hasPermission simplePermission:SeeRevisionInfo ; auth:hasPermission simplePermission:SeeSiteAdminPage ; - auth:hasPermission simplePermission:UseMiscellaneousEditorPages ; + auth:hasPermission simplePermission:UseIndividualControlPanel ; auth:hasPermission simplePermission:PageViewableEditor ; # permissions for ANY logged-in user. @@ -110,7 +110,7 @@ auth:EDITOR auth:hasPermission simplePermission:SeeIndividualEditingPanel ; auth:hasPermission simplePermission:SeeRevisionInfo ; auth:hasPermission simplePermission:SeeSiteAdminPage ; - auth:hasPermission simplePermission:UseMiscellaneousEditorPages ; + auth:hasPermission simplePermission:UseIndividualControlPanel ; auth:hasPermission simplePermission:PageViewableEditor ; # permissions for ANY logged-in user. diff --git a/webapp/rdf/display/everytime/permissions.n3 b/webapp/rdf/display/everytime/permissions.n3 index 6b22e4e13..cbd6a98fb 100644 --- a/webapp/rdf/display/everytime/permissions.n3 +++ b/webapp/rdf/display/everytime/permissions.n3 @@ -133,9 +133,9 @@ action:UseMiscellaneousCuratorPages rdfs:label "USE_MISCELLANEOUS_CURATOR_PAGES" . -action:UseMiscellaneousEditorPages +action:UseIndividualControlPanel a display:RequiredAction ; - rdfs:label "USE_MISCELLANEOUS_EDITOR_PAGES" . + rdfs:label "USE_INDIVIDUAL_CONTROL_PANEL" . action:UseMiscellaneousPages diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index 600fbe95c..2e5d40ca1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -72,6 +72,8 @@ public class SimplePermission extends Permission { NAMESPACE + "SeeVerbosePropertyInformation"); public static final SimplePermission USE_ADVANCED_DATA_TOOLS_PAGES = new SimplePermission( NAMESPACE + "UseAdvancedDataToolsPages"); + public static final SimplePermission USE_INDIVIDUAL_CONTROL_PANEL = new SimplePermission( + NAMESPACE + "UseIndividualControlPanel"); public static final SimplePermission USE_SPARQL_QUERY_PAGE = new SimplePermission( NAMESPACE + "UseSparqlQueryPage"); public static final SimplePermission USE_SPARQL_UPDATE_API = new SimplePermission( @@ -89,8 +91,6 @@ public class SimplePermission extends Permission { NAMESPACE + "UseMiscellaneousAdminPages"); public static final SimplePermission USE_MISCELLANEOUS_CURATOR_PAGES = new SimplePermission( NAMESPACE + "UseMiscellaneousCuratorPages"); - public static final SimplePermission USE_MISCELLANEOUS_EDITOR_PAGES = new SimplePermission( - NAMESPACE + "UseMiscellaneousEditorPages"); public static final SimplePermission USE_MISCELLANEOUS_PAGES = new SimplePermission( NAMESPACE + "UseMiscellaneousPages"); diff --git a/webapp/web/templates/edit/specific/ents_edit.jsp b/webapp/web/templates/edit/specific/ents_edit.jsp index f6f6ba9b2..6289a03f9 100644 --- a/webapp/web/templates/edit/specific/ents_edit.jsp +++ b/webapp/web/templates/edit/specific/ents_edit.jsp @@ -12,7 +12,7 @@ <%@page import="edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission" %> <% - if (PolicyHelper.isAuthorizedForActions(request, SimplePermission.USE_MISCELLANEOUS_EDITOR_PAGES.ACTIONS)) { + if (PolicyHelper.isAuthorizedForActions(request, SimplePermission.USE_INDIVIDUAL_CONTROL_PANEL.ACTIONS)) { request.setAttribute("isEditor", Boolean.TRUE); } %> From 245763e9e7a2d1f11e99c734c5f70ba61311acbf Mon Sep 17 00:00:00 2001 From: j2blake Date: Sat, 4 Jan 2014 14:22:29 -0500 Subject: [PATCH 12/31] Make DeveloperSettings a singleton, and other improvements. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By making it a singleton, we do need an explicit Setup operation. However, it means that we can refer to the settings in client code that doesn’t have access to a request or to the ServletContext. Other refactorings to simplify the logic or make it more scalable. --- .../freemarker/BaseSiteAdminController.java | 2 +- .../config/FreemarkerConfiguration.java | 31 +- .../mannlib/vitro/webapp/i18n/I18n.java | 6 +- .../mannlib/vitro/webapp/i18n/I18nLogger.java | 15 +- .../rdfservice/impl/RDFServiceUtils.java | 2 +- .../impl/logging/LoggingRDFService.java | 16 +- .../logging/LoggingRDFServiceFactory.java | 11 +- .../impl/logging/RDFServiceLogger.java | 61 +-- .../services/shortview/ShortViewLogger.java | 26 +- .../shortview/ShortViewServiceImpl.java | 4 +- .../utils/developer/DeveloperSettings.java | 356 +++++++----------- .../developer/DeveloperSettingsServlet.java | 9 +- .../vitro/webapp/utils/developer/Key.java | 129 +++++++ .../customlistview/CustomListViewLogger.java | 15 +- .../customlistview/PropertyListConfig.java | 3 +- .../developer/DeveloperSettingsStub.java | 33 -- .../javax/servlet/ServletContextStub.java | 7 - .../WEB-INF/resources/startup_listeners.txt | 2 + webapp/web/js/developer/developerPanel.js | 54 +-- .../page/partials/developerPanel.ftl | 26 +- 20 files changed, 387 insertions(+), 421 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java delete mode 100644 webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java index dfcc56ba1..25fd60beb 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/BaseSiteAdminController.java @@ -77,7 +77,7 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet { } if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.ENABLE_DEVELOPER_PANEL.ACTIONS)) { - urls.put("activateDeveloperPanel", "javascript:new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({developerEnabled: true});"); + urls.put("activateDeveloperPanel", "javascript:new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({developer_enabled: true});"); } return urls; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java index 274da8d39..183855b3d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java @@ -26,7 +26,7 @@ import edu.cornell.mannlib.vitro.webapp.freemarker.loader.FreemarkerTemplateLoad import edu.cornell.mannlib.vitro.webapp.i18n.freemarker.I18nMethodModel; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; -import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; import edu.cornell.mannlib.vitro.webapp.web.directives.IndividualShortViewDirective; import edu.cornell.mannlib.vitro.webapp.web.directives.UrlDirective; import edu.cornell.mannlib.vitro.webapp.web.directives.WidgetDirective; @@ -70,7 +70,7 @@ public abstract class FreemarkerConfiguration { confirmInstanceIsSet(); synchronized (instance) { - clearTemplateCacheIfRequested(req); + clearTemplateCacheIfRequested(); keepTemplateLoaderCurrentWithThemeDirectory(req); setThreadLocalsForRequest(req); return instance; @@ -84,21 +84,14 @@ public abstract class FreemarkerConfiguration { } } - private static void clearTemplateCacheIfRequested(HttpServletRequest req) { - if (isTemplateCacheInvalid(req)) { + /** If the developer doesn't want the cache, clear it every time. */ + private static void clearTemplateCacheIfRequested() { + DeveloperSettings settings = DeveloperSettings.getInstance(); + if (settings.getBoolean(Key.DEFEAT_FREEMARKER_CACHE)) { instance.clearTemplateCache(); } } - /** If the developer doesn't want the cache, it's invalid. */ - private static boolean isTemplateCacheInvalid(HttpServletRequest req) { - DeveloperSettings settings = DeveloperSettings.getBean(req); - if (settings.getBoolean(Keys.DEFEAT_FREEMARKER_CACHE)) { - return true; - } - return false; - } - /** * Keep track of the theme directory. If it changes, create an appropriate * new TemplateLoader. @@ -110,7 +103,7 @@ public abstract class FreemarkerConfiguration { HttpServletRequest req) { String themeDir = getThemeDirectory(req); if (hasThemeDirectoryChanged(themeDir) - || haveDeveloperSettingsChanged(req)) { + || haveDeveloperSettingsChanged()) { TemplateLoader tl = createTemplateLoader(req, themeDir); instance.setTemplateLoader(tl); } @@ -131,9 +124,9 @@ public abstract class FreemarkerConfiguration { } } - private static boolean haveDeveloperSettingsChanged(HttpServletRequest req) { - Map settingsMap = DeveloperSettings.getBean(req) - .getSettingsMap(); + private static boolean haveDeveloperSettingsChanged() { + Map settingsMap = DeveloperSettings.getInstance() + .getRawSettingsMap(); if (settingsMap.equals(previousSettingsMap)) { return false; } else { @@ -167,8 +160,8 @@ public abstract class FreemarkerConfiguration { TemplateLoader tl = new MultiTemplateLoader(loaderArray); // If requested, add delimiters to the templates. - DeveloperSettings settings = DeveloperSettings.getBean(req); - if (settings.getBoolean(Keys.INSERT_FREEMARKER_DELIMITERS)) { + DeveloperSettings settings = DeveloperSettings.getInstance(); + if (settings.getBoolean(Key.INSERT_FREEMARKER_DELIMITERS)) { tl = new DelimitingTemplateLoader(tl); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java index 43d33c55e..0bc3a741b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java @@ -22,7 +22,7 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; -import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; /** * Provides access to a bundle of text strings, based on the name of the bundle, @@ -108,7 +108,7 @@ public class I18n { protected I18nBundle getBundle(String bundleName, HttpServletRequest req) { log.debug("Getting bundle '" + bundleName + "'"); - I18nLogger i18nLogger = new I18nLogger(req); + I18nLogger i18nLogger = new I18nLogger(); try { checkDevelopmentMode(req); checkForChangeInThemeDirectory(req); @@ -133,7 +133,7 @@ public class I18n { * If we are in development mode, clear the cache on each request. */ private void checkDevelopmentMode(HttpServletRequest req) { - if (DeveloperSettings.getBean(req).getBoolean(Keys.I18N_DEFEAT_CACHE)) { + if (DeveloperSettings.getInstance().getBoolean(Key.I18N_DEFEAT_CACHE)) { log.debug("In development mode - clearing the cache."); clearCacheOnRequest(req); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nLogger.java index 8889195b6..3ab6575c4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nLogger.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nLogger.java @@ -4,13 +4,11 @@ package edu.cornell.mannlib.vitro.webapp.i18n; import java.util.Arrays; -import javax.servlet.http.HttpServletRequest; - 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.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; /** * If enabled in developer mode, write a message to the log each time someone @@ -23,10 +21,9 @@ public class I18nLogger { private final boolean isLogging; - public I18nLogger(HttpServletRequest req) { - DeveloperSettings settings = DeveloperSettings.getBean(req); - this.isLogging = settings.getBoolean(Keys.ENABLED) - && settings.getBoolean(Keys.I18N_LOG_STRINGS) + public I18nLogger() { + DeveloperSettings settings = DeveloperSettings.getInstance(); + this.isLogging = settings.getBoolean(Key.I18N_LOG_STRINGS) && log.isInfoEnabled(); } @@ -36,11 +33,11 @@ public class I18nLogger { String message = String.format( "Retrieved from %s.%s with %s: '%s'", bundleName, key, Arrays.toString(parameters), rawText); - + if (!rawText.equals(formattedText)) { message += String.format(" --> '%s'", formattedText); } - + log.info(message); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceUtils.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceUtils.java index c71908418..43cfbb6f0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceUtils.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceUtils.java @@ -42,7 +42,7 @@ public class RDFServiceUtils { * Every factory is wrapped in a logger, so we can dynamically * enable or disable logging. */ - return new LoggingRDFServiceFactory(context, factory); + return new LoggingRDFServiceFactory(factory); } else { log.error("Expecting an RDFServiceFactory on the context, but found " + o); return null; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java index 78ccd8a41..cb1651b88 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java @@ -5,8 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging; import java.io.InputStream; import java.util.List; -import javax.servlet.ServletContext; - import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; @@ -19,11 +17,9 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; * For the other methods, it just delegates to the inner RDFService. */ public class LoggingRDFService implements RDFService { - private final ServletContext ctx; private final RDFService innerService; - LoggingRDFService(ServletContext ctx, RDFService innerService) { - this.ctx = ctx; + LoggingRDFService(RDFService innerService) { this.innerService = innerService; } @@ -34,7 +30,7 @@ public class LoggingRDFService implements RDFService { @Override public boolean changeSetUpdate(ChangeSet changeSet) throws RDFServiceException { - try (RDFServiceLogger l = new RDFServiceLogger(ctx, changeSet)) { + try (RDFServiceLogger l = new RDFServiceLogger(changeSet)) { return innerService.changeSetUpdate(changeSet); } } @@ -42,7 +38,7 @@ public class LoggingRDFService implements RDFService { @Override public InputStream sparqlConstructQuery(String query, ModelSerializationFormat resultFormat) throws RDFServiceException { - try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) { + try (RDFServiceLogger l = new RDFServiceLogger(resultFormat, query)) { return innerService.sparqlConstructQuery(query, resultFormat); } } @@ -50,7 +46,7 @@ public class LoggingRDFService implements RDFService { @Override public InputStream sparqlDescribeQuery(String query, ModelSerializationFormat resultFormat) throws RDFServiceException { - try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) { + try (RDFServiceLogger l = new RDFServiceLogger(resultFormat, query)) { return innerService.sparqlDescribeQuery(query, resultFormat); } } @@ -58,14 +54,14 @@ public class LoggingRDFService implements RDFService { @Override public InputStream sparqlSelectQuery(String query, ResultFormat resultFormat) throws RDFServiceException { - try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) { + try (RDFServiceLogger l = new RDFServiceLogger(resultFormat, query)) { return innerService.sparqlSelectQuery(query, resultFormat); } } @Override public boolean sparqlAskQuery(String query) throws RDFServiceException { - try (RDFServiceLogger l = new RDFServiceLogger(ctx, query)) { + try (RDFServiceLogger l = new RDFServiceLogger(query)) { return innerService.sparqlAskQuery(query); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java index 8e97346e4..894b208d1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java @@ -2,8 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging; -import javax.servlet.ServletContext; - import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; @@ -14,23 +12,20 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; * wrapped in a LoggingRDFService. */ public class LoggingRDFServiceFactory implements RDFServiceFactory { - private final ServletContext ctx; private final RDFServiceFactory factory; - public LoggingRDFServiceFactory(ServletContext ctx, - RDFServiceFactory factory) { - this.ctx = ctx; + public LoggingRDFServiceFactory(RDFServiceFactory factory) { this.factory = factory; } @Override public RDFService getRDFService() { - return new LoggingRDFService(ctx, factory.getRDFService()); + return new LoggingRDFService(factory.getRDFService()); } @Override public RDFService getShortTermRDFService() { - return new LoggingRDFService(ctx, factory.getShortTermRDFService()); + return new LoggingRDFService(factory.getShortTermRDFService()); } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java index c0950e967..3d09656f2 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java @@ -9,14 +9,12 @@ import java.util.List; import java.util.ListIterator; import java.util.regex.Pattern; -import javax.servlet.ServletContext; - import org.apache.commons.lang.StringUtils; 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.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; /** * Writes the log message for the LoggingRDFService. @@ -45,7 +43,6 @@ import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; public class RDFServiceLogger implements AutoCloseable { private static final Log log = LogFactory.getLog(RDFServiceLogger.class); - private final ServletContext ctx; private final Object[] args; private boolean isEnabled; @@ -58,31 +55,33 @@ public class RDFServiceLogger implements AutoCloseable { private long startTime; - public RDFServiceLogger(ServletContext ctx, Object... args) { - this.ctx = ctx; + public RDFServiceLogger(Object... args) { this.args = args; - getProperties(); - - if (isEnabled && log.isInfoEnabled()) { - loadStackTrace(); - if (passesQueryRestriction() && passesStackRestriction()) { - this.startTime = System.currentTimeMillis(); + try { + getProperties(); + if (isEnabled && log.isInfoEnabled()) { + loadStackTrace(); + if (passesQueryRestriction() && passesStackRestriction()) { + this.startTime = System.currentTimeMillis(); + } } + } catch (Exception e) { + log.error("Failed to create instance", e); } } private void getProperties() { - DeveloperSettings settings = DeveloperSettings.getBean(ctx); - isEnabled = settings.getBoolean(Keys.LOGGING_RDF_ENABLE); - traceRequested = settings.getBoolean(Keys.LOGGING_RDF_STACK_TRACE); + DeveloperSettings settings = DeveloperSettings.getInstance(); + isEnabled = settings.getBoolean(Key.LOGGING_RDF_ENABLE); + traceRequested = settings.getBoolean(Key.LOGGING_RDF_STACK_TRACE); queryStringRestriction = patternFromSettings(settings, - Keys.LOGGING_RDF_QUERY_RESTRICTION); + Key.LOGGING_RDF_QUERY_RESTRICTION); callStackRestriction = patternFromSettings(settings, - Keys.LOGGING_RDF_STACK_RESTRICTION); + Key.LOGGING_RDF_STACK_RESTRICTION); } - private Pattern patternFromSettings(DeveloperSettings settings, Keys key) { + private Pattern patternFromSettings(DeveloperSettings settings, Key key) { String patternString = settings.getString(key); if (StringUtils.isBlank(patternString)) { return null; @@ -160,13 +159,13 @@ public class RDFServiceLogger implements AutoCloseable { } private String assembleQueryString() { - StringBuilder query = new StringBuilder(); + List stringArgs = new ArrayList<>(); for (Object arg : args) { if (arg instanceof String) { - query.append((String) arg).append(" "); + stringArgs.add((String) arg); } } - return query.deleteCharAt(query.length() - 1).toString(); + return StringUtils.join(stringArgs, " "); } private boolean passesStackRestriction() { @@ -188,16 +187,20 @@ public class RDFServiceLogger implements AutoCloseable { @Override public void close() { - if (startTime != 0L) { - long endTime = System.currentTimeMillis(); + try { + if (startTime != 0L) { + long endTime = System.currentTimeMillis(); - float elapsedSeconds = (endTime - startTime) / 1000.0F; - String cleanArgs = Arrays.deepToString(args).replaceAll( - "[\\n\\r\\t]+", " "); - String formattedTrace = formatStackTrace(); + float elapsedSeconds = (endTime - startTime) / 1000.0F; + String cleanArgs = Arrays.deepToString(args).replaceAll( + "[\\n\\r\\t]+", " "); + String formattedTrace = formatStackTrace(); - log.info(String.format("%8.3f %s %s %s", elapsedSeconds, - methodName, cleanArgs, formattedTrace)); + log.info(String.format("%8.3f %s %s %s", elapsedSeconds, + methodName, cleanArgs, formattedTrace)); + } + } catch (Exception e) { + log.error("Failed to write log record", e); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java index 220185445..0cc864ed2 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewLogger.java @@ -6,10 +6,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.beans.Individual; -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; -import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; /** * When we use a short view other than the default, log it. @@ -17,31 +16,26 @@ import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; public class ShortViewLogger { private static final Log log = LogFactory.getLog(ShortViewLogger.class); - public static void log(VitroRequest vreq, String contextName, - Individual individual, String classUri, TemplateAndDataGetters tdg) { - if (isLogging(vreq)) { + public static void log(String contextName, Individual individual, + String classUri, TemplateAndDataGetters tdg) { + if (isLogging()) { log.info("Using custom short view in " + contextName + " because '" + individual.getURI() + "' (" + individual.getLabel() + ") has type '" + classUri + "': " + tdg); } } - public static void log(VitroRequest vreq, String contextName, - Individual individual) { - if (isLogging(vreq)) { + public static void log(String contextName, Individual individual) { + if (isLogging()) { log.info("Using default short view in " + contextName + " for '" + individual.getURI() + "' (" + individual.getLabel() + ")"); } } - private static boolean isLogging(VitroRequest vreq) { - if (!log.isInfoEnabled()) { - return false; - } - DeveloperSettings settings = DeveloperSettings.getBean(vreq); - return settings.getBoolean(Keys.ENABLED) - && settings - .getBoolean(Keys.PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW); + private static boolean isLogging() { + return log.isInfoEnabled() + && DeveloperSettings.getInstance().getBoolean( + Key.PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java index c072f866e..5ea21025d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewServiceImpl.java @@ -114,13 +114,13 @@ public class ShortViewServiceImpl implements ShortViewService { TemplateAndDataGetters tdg = faker.getShortViewProperties(vreq, individual, classUri, svContext.name()); if (tdg != null) { - ShortViewLogger.log(vreq, svContext.name(), individual, classUri, tdg); + ShortViewLogger.log(svContext.name(), individual, classUri, tdg); return tdg; } } // Didn't find one? Use the default values. - ShortViewLogger.log(vreq, svContext.name(), individual); + ShortViewLogger.log(svContext.name(), individual); return new TemplateAndDataGetters(svContext.getDefaultTemplateName()); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java index f883281f9..dfc6727a5 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java @@ -3,6 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.utils.developer; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; import java.nio.file.Paths; import java.util.Arrays; @@ -13,251 +14,92 @@ import java.util.Map; import java.util.Properties; import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; /** - * Hold the global developer settings. Render to JSON when requested. + * A singleton holder for the developer settings. * - * On first request, the "developer.properties" file is loaded from the Vitro - * home directory. If the file doesn't exist, or doesn't contain values for - * certain properties, those propertiew will keep their default values. + * Start with an empty settings map. + * + * The Setup class will read "developer.properties" from the Vitro home + * directory, and load its settings. If the file doesn't exist, or doesn't + * contain values for certain properties, those propertiew will keep their + * default values. * * An AJAX request can be used to update the properties. If the request has * multiple values for a property, the first value will be used. If the request * does not contain a value for a property, that property will keep its current * value. + * + * The property names in "developer.properties" are not suitable as fields in + * the HTML panel, because they contain periods. For the HTML panel, we + * translate those periods to underscores. + * + * If the ENABLED flag is not set, then getBinary() will return false for all + * keys, and getString() will return the empty string. This simplifies the logic + * in the client code. Use getRawSettingsMap() to display the actual values in + * the developer panel. */ public class DeveloperSettings { private static final Log log = LogFactory.getLog(DeveloperSettings.class); - public enum Keys { - /** - * Developer mode and developer panel is enabled. - */ - ENABLED("developer.enabled", true), - - /** - * Users don't need authority to use the developer panel. But they still - * can't enable it without authority. - */ - PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true), - - /** - * Load Freemarker templates every time they are requested. - */ - DEFEAT_FREEMARKER_CACHE("developer.defeatFreemarkerCache", true), - - /** - * Show where each Freemarker template starts and stops. - */ - INSERT_FREEMARKER_DELIMITERS("developer.insertFreemarkerDelimiters", - true), - - /** - * Load language property files every time they are requested. - */ - I18N_DEFEAT_CACHE("developer.i18n.defeatCache", true), - - /** - * Enable the I18nLogger to log each string request. - */ - I18N_LOG_STRINGS("developer.i18n.logStringRequests", true), - - /** - * Enable the LoggingRDFService - */ - LOGGING_RDF_ENABLE("developer.loggingRDFService.enable", true), - - /** - * When logging with the LoggingRDFService, include a stack trace - */ - LOGGING_RDF_STACK_TRACE("developer.loggingRDFService.stackTrace", true), - - /** - * Don't log with the LoggingRDFService unless the calling stack meets - * this restriction. - */ - LOGGING_RDF_QUERY_RESTRICTION( - "developer.loggingRDFService.queryRestriction", false), - - /** - * Don't log with the LoggingRDFService unless the calling stack meets - * this restriction. - */ - LOGGING_RDF_STACK_RESTRICTION( - "developer.loggingRDFService.stackRestriction", false), - - /** - * Tell the CustomListViewLogger to note the use of non-default custom - * list views. - */ - PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW( - "developer.pageContents.logCustomListView", true), - - /** - * Tell the ShortViewLogger to note the use of non-default short views. - */ - PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW( - "developer.pageContents.logCustomShortView", true); - - private final String propertyName; - private final String elementId; - private final boolean bool; - - private Keys(String propertyName, boolean bool) { - this.propertyName = propertyName; - this.elementId = produceElementId(); - this.bool = bool; - } - - public String propertyName() { - return propertyName; - } - - public String elementId() { - return elementId; - } - - boolean isBoolean() { - return bool; - } - - /** - * The element ID is camel-case instead of period-delimited. So - * "developer.enabled" becomes "developerEnabled". - */ - String produceElementId() { - StringBuilder id = new StringBuilder(propertyName.length()); - boolean capitalize = false; - for (int i = 0; i < propertyName.length(); i++) { - char c = propertyName.charAt(i); - if (c == '.') { - capitalize = true; - } else if (capitalize) { - id.append(Character.toUpperCase(c)); - capitalize = false; - } else { - id.append(c); - } - } - return id.toString(); - } - - @Override - public String toString() { - return propertyName; - } - - static Keys fromElementId(String id) { - for (Keys k : Keys.values()) { - if (k.elementId.equals(id)) { - return k; - } - } - log.error("Can't find key for element id: '" + id + "'"); - return null; - } - - static Keys fromPropertyName(String name) { - for (Keys k : Keys.values()) { - if (k.propertyName.equals(name)) { - return k; - } - } - log.error("Can't find key for property name: '" + name + "'"); - return null; - } - - } - // ---------------------------------------------------------------------- // The factory // ---------------------------------------------------------------------- - protected static final String ATTRIBUTE_NAME = DeveloperSettings.class - .getName(); + private static final DeveloperSettings instance = new DeveloperSettings(); - public static DeveloperSettings getBean(HttpServletRequest req) { - return getBean(req.getSession().getServletContext()); - } - - public static DeveloperSettings getBean(ServletContext ctx) { - Object o = ctx.getAttribute(ATTRIBUTE_NAME); - if (o instanceof DeveloperSettings) { - return (DeveloperSettings) o; - } else { - DeveloperSettings ds = new DeveloperSettings(ctx); - ctx.setAttribute(ATTRIBUTE_NAME, ds); - return ds; - } + public static DeveloperSettings getInstance() { + return instance; } // ---------------------------------------------------------------------- // The instance // ---------------------------------------------------------------------- - private final Map settings = new EnumMap<>(Keys.class); + private final Map settings; - protected DeveloperSettings(ServletContext ctx) { - updateFromFile(ctx); + private DeveloperSettings() { + this.settings = new EnumMap<>(Key.class); } - /** - * Read the initial settings from "developer.properties" in the Vitro home - * directory. - * - * This method is "protected" so we can override it for unit tests. - */ - protected void updateFromFile(ServletContext ctx) { - Map fromFile = new HashMap<>(); - - ConfigurationProperties props = ConfigurationProperties.getBean(ctx); - String home = props.getProperty("vitro.home"); - File dsFile = Paths.get(home, "developer.properties").toFile(); - - if (dsFile.isFile()) { - try (FileReader reader = new FileReader(dsFile)) { - Properties dsProps = new Properties(); - dsProps.load(reader); - for (String key : dsProps.stringPropertyNames()) { - fromFile.put(Keys.fromPropertyName(key), - dsProps.getProperty(key)); - } - } catch (Exception e) { - log.warn("Failed to load 'developer.properties' file.", e); - } - } else { - log.debug("No developer.properties file."); - } - - log.debug("Properties from file: " + fromFile); - update(fromFile); - } - - /** Provide the parameter map from the HttpServletRequest */ public void updateFromRequest(Map parameterMap) { if (log.isDebugEnabled()) { dumpParameterMap(parameterMap); } - Map fromRequest = new HashMap<>(); + Map fromRequest = new HashMap<>(); for (String key : parameterMap.keySet()) { - fromRequest.put(Keys.fromElementId(key), parameterMap.get(key)[0]); + fromRequest.put(Key.fromElementId(key), parameterMap.get(key)[0]); } update(fromRequest); } - private void update(Map changedSettings) { - for (Keys key : Keys.values()) { + public void updateFromProperties(Properties properties) { + Map fromFile = new HashMap<>(); + for (String key : properties.stringPropertyNames()) { + fromFile.put(Key.fromPropertyName(key), properties.getProperty(key)); + } + update(fromFile); + } + + /** + * Update by known keys, so we will ignore any irrelevant request + * parameters, or incorrect properties. + */ + private void update(Map changedSettings) { + for (Key key : Key.values()) { String s = changedSettings.get(key); if (s != null) { if (key.isBoolean()) { - settings.put(key, Boolean.valueOf(s)); + settings.put(key, Boolean.valueOf(s).toString()); } else { settings.put(key, s); } @@ -266,48 +108,68 @@ public class DeveloperSettings { log.debug("DeveloperSettings: " + this); } - public Object get(Keys key) { - if (key.isBoolean()) { - return getBoolean(key); - } else { - return getString(key); - } - } - - public boolean getBoolean(Keys key) { + /** + * If developerMode is enabled, return the boolean value of the stored + * setting. + */ + public boolean getBoolean(Key key) { if (!key.isBoolean()) { - throw new IllegalArgumentException("Key '" + key - + "' does not take a boolean value."); + log.warn("Key '" + key + "' does not take a boolean value."); } - if (settings.containsKey(key)) { - if (Boolean.TRUE.equals(settings.get(Keys.ENABLED))) { - return (Boolean) settings.get(key); - } + if (isDeveloperModeEnabled()) { + return Boolean.valueOf(settings.get(key)); + } else { + return false; } - return false; } - public String getString(Keys key) { + /** + * If developerMode is enabled and the setting has a value, return that + * value. Otherwise, return the empty string. + */ + public String getString(Key key) { if (key.isBoolean()) { - throw new IllegalArgumentException("Key '" + key - + "' takes a boolean value."); + log.warn("Key '" + key + "' takes a boolean value."); } - if (settings.containsKey(key)) { - if (Boolean.TRUE.equals(settings.get(Keys.ENABLED))) { - return (String) settings.get(key); - } + String value = settings.get(key); + if (value != null && isDeveloperModeEnabled()) { + return value; + } else { + return ""; } - return ""; } - public Map getSettingsMap() { + private boolean isDeveloperModeEnabled() { + return Boolean.valueOf(settings.get(Key.ENABLED)); + } + + /** + * Get the values of all the settings, by element ID, regardless of whether + * developerMode is enabled or not. Boolean settings are represented as + * actual Booleans, so Freemarker can perform logical tests on them. + */ + public Map getRawSettingsMap() { Map map = new HashMap<>(); - for (Keys key : Keys.values()) { - map.put(key.elementId(), get(key)); + for (Key key : Key.values()) { + map.put(key.elementId(), getRawValue(key)); } return map; } + /** + * Get a String or Boolean value, as appropriate for the key. A boolean key + * with no value returns false. A non-boolean key with no value returns the + * empty string. + */ + private Object getRawValue(Key key) { + String value = settings.get(key); + if (key.isBoolean()) { + return Boolean.valueOf(value); + } else { + return (value == null) ? "" : value; + } + } + @Override public String toString() { return "DeveloperSettings" + settings; @@ -322,4 +184,42 @@ public class DeveloperSettings { log.debug("Parameter map: " + map); } + // ---------------------------------------------------------------------- + // Setup class + // ---------------------------------------------------------------------- + + public static class Setup implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + ServletContext ctx = sce.getServletContext(); + StartupStatus ss = StartupStatus.getBean(ctx); + ConfigurationProperties props = ConfigurationProperties + .getBean(ctx); + DeveloperSettings devSettings = DeveloperSettings.getInstance(); + + String home = props.getProperty("vitro.home"); + File dsFile = Paths.get(home, "developer.properties").toFile(); + + try (FileReader reader = new FileReader(dsFile)) { + Properties dsProps = new Properties(); + dsProps.load(reader); + devSettings.updateFromProperties(dsProps); + ss.info(this, "Loaded the 'developer.properties' file: " + + devSettings); + } catch (FileNotFoundException e) { + ss.info(this, "'developer.properties' file does not exist."); + } catch (Exception e) { + ss.warning(this, + "Failed to load the 'developer.properties' file.", e); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + // Nothing to remove. + } + + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsServlet.java index 7422137ab..09460d348 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsServlet.java @@ -2,7 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.utils.developer; -import static edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys.PERMIT_ANONYMOUS_CONTROL; +import static edu.cornell.mannlib.vitro.webapp.utils.developer.Key.PERMIT_ANONYMOUS_CONTROL; import java.io.IOException; import java.util.HashMap; @@ -42,7 +42,7 @@ public class DeveloperSettingsServlet extends VitroAjaxController { @Override protected void doRequest(VitroRequest vreq, HttpServletResponse resp) throws ServletException, IOException { - DeveloperSettings settings = DeveloperSettings.getBean(vreq); + DeveloperSettings settings = DeveloperSettings.getInstance(); /* * Are they allowed to control the panel? @@ -70,8 +70,9 @@ public class DeveloperSettingsServlet extends VitroAjaxController { private Map buildBodyMap(boolean authorized, DeveloperSettings settings) { Map settingsMap = new HashMap<>(); - settingsMap.putAll(settings.getSettingsMap()); + settingsMap.putAll(settings.getRawSettingsMap()); settingsMap.put("mayControl", authorized); + Map bodyMap = new HashMap<>(); bodyMap.put("settings", settingsMap); return bodyMap; @@ -84,7 +85,7 @@ public class DeveloperSettingsServlet extends VitroAjaxController { } private boolean isAuthorized(VitroRequest vreq) { - boolean authBySetting = DeveloperSettings.getBean(vreq).getBoolean( + boolean authBySetting = DeveloperSettings.getInstance().getBoolean( PERMIT_ANONYMOUS_CONTROL); boolean authByPolicy = PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.ENABLE_DEVELOPER_PANEL.ACTION); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java new file mode 100644 index 000000000..d519f6b69 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java @@ -0,0 +1,129 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.developer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Keys for the DeveloperSettings. Each key holds these values: + * + * A property name, which specifies the key in developer.properties, like + * "this.thatThing" + * + * A flag to say whether this key controls a boolean value. If false, then the + * value is a string. + * + * We can derive the element ID for each key by replacing the periods in the + * property name with underscores. + */ +public enum Key { + /** + * Developer mode and developer panel is enabled. + */ + ENABLED("developer.enabled", true), + + /** + * If the developer panel is enabled, can a non-logged-in user change the + * settings? + */ + PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true), + + /** + * Load Freemarker templates every time they are requested. + */ + DEFEAT_FREEMARKER_CACHE("developer.defeatFreemarkerCache", true), + + /** + * Show where each Freemarker template starts and stops. + */ + INSERT_FREEMARKER_DELIMITERS("developer.insertFreemarkerDelimiters", true), + + /** + * Load language property files every time they are requested. + */ + I18N_DEFEAT_CACHE("developer.i18n.defeatCache", true), + + /** + * Enable the I18nLogger to log each string request. + */ + I18N_LOG_STRINGS("developer.i18n.logStringRequests", true), + + /** + * Enable the LoggingRDFService + */ + LOGGING_RDF_ENABLE("developer.loggingRDFService.enable", true), + + /** + * When logging with the LoggingRDFService, include a stack trace + */ + LOGGING_RDF_STACK_TRACE("developer.loggingRDFService.stackTrace", true), + + /** + * Don't log with the LoggingRDFService unless the calling stack meets this + * restriction. + */ + LOGGING_RDF_QUERY_RESTRICTION( + "developer.loggingRDFService.queryRestriction", false), + + /** + * Don't log with the LoggingRDFService unless the calling stack meets this + * restriction. + */ + LOGGING_RDF_STACK_RESTRICTION( + "developer.loggingRDFService.stackRestriction", false), + + /** + * Tell the CustomListViewLogger to note the use of non-default custom list + * views. + */ + PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW( + "developer.pageContents.logCustomListView", true), + + /** + * Tell the ShortViewLogger to note the use of non-default short views. + */ + PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW( + "developer.pageContents.logCustomShortView", true); + + private static final Log log = LogFactory.getLog(Key.class); + private final String propertyName; + private final boolean bool; + + private Key(String propertyName, boolean bool) { + this.propertyName = propertyName; + this.bool = bool; + } + + public String propertyName() { + return propertyName; + } + + public String elementId() { + return propertyName.replace('.', '_'); + } + + boolean isBoolean() { + return bool; + } + + @Override + public String toString() { + return propertyName; + } + + static Key fromElementId(String id) { + return fromPropertyName(id.replace('_', '.')); + } + + static Key fromPropertyName(String name) { + for (Key k : Key.values()) { + if (k.propertyName.equals(name)) { + return k; + } + } + log.error("Can't find key for property name: '" + name + "'"); + return null; + } + +} \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java index 2509af61c..00e095efb 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/CustomListViewLogger.java @@ -6,9 +6,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; -import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings.Keys; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; /** * If enabled in the developer settings (and log levels), log every non-default @@ -18,21 +17,19 @@ public class CustomListViewLogger { private static final Log log = LogFactory .getLog(CustomListViewLogger.class); - public static void log(VitroRequest vreq, ObjectProperty op, - String configFileName) { - if (isLogging(vreq)) { + public static void log(ObjectProperty op, String configFileName) { + if (isLogging()) { log.info("Using list view: '" + configFileName + "' for " + op.getURI() + " (" + op.getLabel() + ")"); } } - private static boolean isLogging(VitroRequest vreq) { + private static boolean isLogging() { if (!log.isInfoEnabled()) { return false; } - DeveloperSettings settings = DeveloperSettings.getBean(vreq); - return settings.getBoolean(Keys.ENABLED) - && settings.getBoolean(Keys.PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW); + DeveloperSettings settings = DeveloperSettings.getInstance(); + return settings.getBoolean(Key.PAGE_CONTENTS_LOG_CUSTOM_LIST_VIEW); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java index ad645f393..fa212fd64 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java @@ -21,7 +21,6 @@ import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.ObjectProp import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.ObjectPropertyTemplateModel; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.ObjectPropertyTemplateModel.ConfigError; import freemarker.cache.TemplateLoader; -import freemarker.template.Configuration; public class PropertyListConfig { private static final Log log = LogFactory.getLog(PropertyListConfig.class); @@ -62,7 +61,7 @@ public class PropertyListConfig { if (configFileName == null) { // no custom config; use default config configFileName = DEFAULT_CONFIG_FILE_NAME; } else { - CustomListViewLogger.log(vreq, op, configFileName); + CustomListViewLogger.log(op, configFileName); } log.debug("Using list view config file " + configFileName + " for object property " + op.getURI()); diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java deleted file mode 100644 index 6e2871596..000000000 --- a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java +++ /dev/null @@ -1,33 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package stubs.edu.cornell.mannlib.vitro.webapp.utils.developer; - -import javax.servlet.ServletContext; - -import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; - -/** - * Do everything that a standard DeveloperSettings would do, except loading from - * a properties file. - * - * That way, we don't require ConfigurationProperties to find the Vitro home - * directory, so we don't throw errors if there is no ConfigurationProperties. - */ -public class DeveloperSettingsStub extends DeveloperSettings { - /** - * Factory method. Create the stub and set it into the ServletContext. - */ - public static void set(ServletContext ctx) { - ctx.setAttribute(ATTRIBUTE_NAME, new DeveloperSettingsStub(ctx)); - } - - protected DeveloperSettingsStub(ServletContext ctx) { - super(ctx); - } - - @Override - protected void updateFromFile(ServletContext ctx) { - // Don't bother. - } - -} diff --git a/webapp/test/stubs/javax/servlet/ServletContextStub.java b/webapp/test/stubs/javax/servlet/ServletContextStub.java index eb1e5eab7..46e592d60 100644 --- a/webapp/test/stubs/javax/servlet/ServletContextStub.java +++ b/webapp/test/stubs/javax/servlet/ServletContextStub.java @@ -21,8 +21,6 @@ import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import stubs.edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettingsStub; - /** * A simple stand-in for the {@link ServletContext}, for use in unit tests. */ @@ -38,11 +36,6 @@ public class ServletContextStub implements ServletContext { private final Map mockResources = new HashMap(); private final Map realPaths = new HashMap(); - public ServletContextStub() { - // Assume that unit tests won't want to use Developer mode. - DeveloperSettingsStub.set(this); - } - public void setContextPath(String contextPath) { if (contextPath == null) { throw new NullPointerException("contextPath may not be null."); diff --git a/webapp/web/WEB-INF/resources/startup_listeners.txt b/webapp/web/WEB-INF/resources/startup_listeners.txt index 9129ea5ca..1744c622f 100644 --- a/webapp/web/WEB-INF/resources/startup_listeners.txt +++ b/webapp/web/WEB-INF/resources/startup_listeners.txt @@ -11,6 +11,8 @@ edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesSetup edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesSmokeTests +edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings$Setup + edu.cornell.mannlib.vitro.webapp.config.RevisionInfoSetup edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory$Setup diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index 201168ba9..9f8cabff8 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -34,41 +34,41 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developerPanelSaveButton").onclick = function() { updateDeveloperPanel(collectFormData()); } - document.getElementById("developerEnabled").onchange = updateDisabledFields - document.getElementById("developerLoggingRDFServiceEnable").onchange = updateDisabledFields + document.getElementById("developer_enabled").onchange = updateDisabledFields + document.getElementById("developer_loggingRDFService_enable").onchange = updateDisabledFields } function updateDisabledFields() { - var developerEnabled = document.getElementById("developerEnabled").checked; - document.getElementById("developerPermitAnonymousControl").disabled = !developerEnabled; - document.getElementById("developerDefeatFreemarkerCache").disabled = !developerEnabled; - document.getElementById("developerInsertFreemarkerDelimiters").disabled = !developerEnabled; - document.getElementById("developerPageContentsLogCustomListView").disabled = !developerEnabled; - document.getElementById("developerPageContentsLogCustomShortView").disabled = !developerEnabled; - document.getElementById("developerI18nDefeatCache").disabled = !developerEnabled; - document.getElementById("developerI18nLogStringRequests").disabled = !developerEnabled; - document.getElementById("developerLoggingRDFServiceEnable").disabled = !developerEnabled; + var developerEnabled = document.getElementById("developer_enabled").checked; + document.getElementById("developer_permitAnonymousControl").disabled = !developerEnabled; + document.getElementById("developer_defeatFreemarkerCache").disabled = !developerEnabled; + document.getElementById("developer_insertFreemarkerDelimiters").disabled = !developerEnabled; + document.getElementById("developer_pageContents_logCustomListView").disabled = !developerEnabled; + document.getElementById("developer_pageContents_logCustomShortView").disabled = !developerEnabled; + document.getElementById("developer_i18n_defeatCache").disabled = !developerEnabled; + document.getElementById("developer_i18n_logStringRequests").disabled = !developerEnabled; + document.getElementById("developer_loggingRDFService_enable").disabled = !developerEnabled; - var rdfServiceEnabled = developerEnabled && document.getElementById("developerLoggingRDFServiceEnable").checked; - document.getElementById("developerLoggingRDFServiceStackTrace").disabled = !rdfServiceEnabled; - document.getElementById("developerLoggingRDFServiceQueryRestriction").disabled = !rdfServiceEnabled; - document.getElementById("developerLoggingRDFServiceStackRestriction").disabled = !rdfServiceEnabled; + var rdfServiceEnabled = developerEnabled && document.getElementById("developer_loggingRDFService_enable").checked; + document.getElementById("developer_loggingRDFService_stackTrace").disabled = !rdfServiceEnabled; + document.getElementById("developer_loggingRDFService_queryRestriction").disabled = !rdfServiceEnabled; + document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled; } function collectFormData() { var data = new Object(); - getCheckbox("developerEnabled", data); - getCheckbox("developerPermitAnonymousControl", data); - getCheckbox("developerDefeatFreemarkerCache", data); - getCheckbox("developerInsertFreemarkerDelimiters", data); - getCheckbox("developerPageContentsLogCustomListView", data); - getCheckbox("developerPageContentsLogCustomShortView", data); - getCheckbox("developerI18nDefeatCache", data); - getCheckbox("developerI18nLogStringRequests", data); - getCheckbox("developerLoggingRDFServiceEnable", data); - getCheckbox("developerLoggingRDFServiceStackTrace", data); - getText("developerLoggingRDFServiceQueryRestriction", data); - getText("developerLoggingRDFServiceStackRestriction", data); + getCheckbox("developer_enabled", data); + getCheckbox("developer_permitAnonymousControl", data); + getCheckbox("developer_defeatFreemarkerCache", data); + getCheckbox("developer_insertFreemarkerDelimiters", data); + getCheckbox("developer_pageContents_logCustomListView", data); + getCheckbox("developer_pageContents_logCustomShortView", data); + getCheckbox("developer_i18n_defeatCache", data); + getCheckbox("developer_i18n_logStringRequests", data); + getCheckbox("developer_loggingRDFService_enable", data); + getCheckbox("developer_loggingRDFService_stackTrace", data); + getText("developer_loggingRDFService_queryRestriction", data); + getText("developer_loggingRDFService_stackRestriction", data); return data; } diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index 9b8b3ca3f..2528912cc 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -9,7 +9,7 @@ -<#if !settings.developerEnabled> +<#if !settings.developer_enabled> <#elseif !settings.mayControl>

${siteName} is running in developer mode.

@@ -22,11 +22,11 @@
@@ -35,11 +35,11 @@
Page configuration
@@ -47,11 +47,11 @@
Language support
@@ -71,11 +71,11 @@
Freemarker templates
@@ -83,21 +83,21 @@
SPARQL Queries
From 23c30a8957c00887502f5d15953598b24daa90fa Mon Sep 17 00:00:00 2001 From: j2blake Date: Sun, 5 Jan 2014 17:18:37 -0500 Subject: [PATCH 13/31] Add tab structure to the developer panel --- webapp/web/css/developer/developerPanel.css | 25 +++ webapp/web/js/developer/developerPanel.js | 12 +- .../freemarker/page/partials/developer.ftl | 7 +- .../page/partials/developerPanel.ftl | 150 ++++++++++-------- 4 files changed, 123 insertions(+), 71 deletions(-) diff --git a/webapp/web/css/developer/developerPanel.css b/webapp/web/css/developer/developerPanel.css index ae61e5401..b93ca7288 100644 --- a/webapp/web/css/developer/developerPanel.css +++ b/webapp/web/css/developer/developerPanel.css @@ -46,3 +46,28 @@ div.developer input[type="text"]:disabled { div.developer a { margin: 3px; } + +#developerPanelBody ul.ui-tabs-nav li a { + padding: 0em 1em; +} + +#developerPanelBody .ui-widget-header { + background-image: none; + background-color: #CC9900; +} + +#developerPanelBody .ui-widget-content { + background-image: none; + background-color: #f7dd8a; +} + +#developerPanelBody .ui-widget-header .ui-state-default { + background-image: none; + background-color: #f7cc33; +} + +#developerPanelBody .ui-widget-header li.ui-state-active { + background-image: none; + background-color: #f7dd8a; +} + diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index 9f8cabff8..bd7e35c40 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -14,6 +14,7 @@ function DeveloperPanel(developerAjaxUrl) { enablePanelOpener(); addBehaviorToElements(); updateDisabledFields(); + initializeTabs(); } } }); @@ -55,6 +56,10 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled; } + function initializeTabs() { + $("#developerTabs").tabs(); + } + function collectFormData() { var data = new Object(); getCheckbox("developer_enabled", data); @@ -82,9 +87,14 @@ function DeveloperPanel(developerAjaxUrl) { } /* - * Relies on the global variable for the AJAX URL. + * Relies on the global variables for the AJAX URL and the CSS files. */ $(document).ready(function() { + $.each(developerCssLinks, function(index, value){ + var cssLink = $(""); + $("head").append(cssLink); + }); + new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({}); }); diff --git a/webapp/web/templates/freemarker/page/partials/developer.ftl b/webapp/web/templates/freemarker/page/partials/developer.ftl index 115718197..efc60beee 100644 --- a/webapp/web/templates/freemarker/page/partials/developer.ftl +++ b/webapp/web/templates/freemarker/page/partials/developer.ftl @@ -1,6 +1,9 @@ <#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
- + ${scripts.add('')} - \ No newline at end of file +${scripts.add('')} diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index 2528912cc..013d15973 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -31,75 +31,89 @@
-
-
- Page configuration - - -
- -
- Language support - - -
- - -
- -
-
- Freemarker templates - - -
- -
- SPARQL Queries - -
- - - +
+ + +
+
+
+ Page configuration + + +
+ +
+ Language support + + +
+ +
+ +
+
+ Freemarker templates + + +
+ +
+ SPARQL Queries + +
+ + + +
+
+
+
+ +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
From 0f0cac5d352d68a1bb0f300b8f2d99240d1e89d3 Mon Sep 17 00:00:00 2001 From: j2blake Date: Sun, 5 Jan 2014 17:19:23 -0500 Subject: [PATCH 14/31] Use JQuery to simplify some of the behavior code in the developer panel --- webapp/web/js/developer/developerPanel.js | 61 ++++++++--------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index bd7e35c40..684b96dc0 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -3,18 +3,17 @@ function DeveloperPanel(developerAjaxUrl) { this.setupDeveloperPanel = updateDeveloperPanel; - function updateDeveloperPanel(data) { + function updateDeveloperPanel() { $.ajax({ url: developerAjaxUrl, dataType: "json", - data: data, + data: collectFormData(), complete: function(xhr, status) { updatePanelContents(xhr.responseText); if (document.getElementById("developerPanelSaveButton")) { - enablePanelOpener(); + initializeTabs(); addBehaviorToElements(); updateDisabledFields(); - initializeTabs(); } } }); @@ -24,19 +23,19 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developerPanel").innerHTML = contents; } - function enablePanelOpener() { - document.getElementById("developerPanelClickMe").onclick = function() { - document.getElementById("developerPanelClickText").style.display = "none"; - document.getElementById("developerPanelBody").style.display = "block"; - }; + function initializeTabs() { + $("#developerTabs").tabs(); + } + + function addBehaviorToElements() { + $( "#developerPanelClickMe" ).click(openPanel); + $( "#developerPanelSaveButton" ).click(updateDeveloperPanel); + $( "#developerPanelBody [type=checkbox]" ).change(updateDisabledFields); } - function addBehaviorToElements() { - document.getElementById("developerPanelSaveButton").onclick = function() { - updateDeveloperPanel(collectFormData()); - } - document.getElementById("developer_enabled").onchange = updateDisabledFields - document.getElementById("developer_loggingRDFService_enable").onchange = updateDisabledFields + function openPanel() { + $( "#developerPanelClickText" ).hide(); + $( "#developerPanelBody" ).css( "display", "block" ); } function updateDisabledFields() { @@ -56,34 +55,16 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled; } - function initializeTabs() { - $("#developerTabs").tabs(); - } - function collectFormData() { var data = new Object(); - getCheckbox("developer_enabled", data); - getCheckbox("developer_permitAnonymousControl", data); - getCheckbox("developer_defeatFreemarkerCache", data); - getCheckbox("developer_insertFreemarkerDelimiters", data); - getCheckbox("developer_pageContents_logCustomListView", data); - getCheckbox("developer_pageContents_logCustomShortView", data); - getCheckbox("developer_i18n_defeatCache", data); - getCheckbox("developer_i18n_logStringRequests", data); - getCheckbox("developer_loggingRDFService_enable", data); - getCheckbox("developer_loggingRDFService_stackTrace", data); - getText("developer_loggingRDFService_queryRestriction", data); - getText("developer_loggingRDFService_stackRestriction", data); + $( "#developerPanelBody [type=checkbox]" ).each(function(i, element){ + data[element.id] = element.checked; + }); + $( "#developerPanelBody [type=text]" ).each(function(i, element){ + data[element.id] = element.value; + }); return data; } - - function getCheckbox(key, dest) { - dest[key] = document.getElementById(key).checked; - } - - function getText(key, dest) { - dest[key] = document.getElementById(key).value; - } } /* @@ -95,6 +76,6 @@ $(document).ready(function() { $("head").append(cssLink); }); - new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel({}); + new DeveloperPanel(developerAjaxUrl).setupDeveloperPanel(); }); From 54509b1aeeda0da5ffde620c56df6ded5554c993 Mon Sep 17 00:00:00 2001 From: j2blake Date: Tue, 7 Jan 2014 15:08:30 -0500 Subject: [PATCH 15/31] VIVO-660 Add an Authorization tabs to the developer panel --- .../auth/policy/PolicyDecisionLogger.java | 176 ++++++++++++++++++ .../vitro/webapp/auth/policy/PolicyList.java | 13 +- .../utils/developer/DeveloperSettings.java | 1 + .../vitro/webapp/utils/developer/Key.java | 42 ++++- webapp/web/css/developer/developerPanel.css | 3 + webapp/web/js/developer/developerPanel.js | 8 + .../page/partials/developerPanel.ftl | 102 +++++----- 7 files changed, 285 insertions(+), 60 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyDecisionLogger.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyDecisionLogger.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyDecisionLogger.java new file mode 100644 index 000000000..6748a909d --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyDecisionLogger.java @@ -0,0 +1,176 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.policy; + +import static edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Authorization.INCONCLUSIVE; + +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle; +import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyDecision; +import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyIface; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; +import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; + +/** + * If enabled in the developer settings (and log levels), log each + * PolicyDecision (subject to restrictions). + * + * Some restrictions apply to the logger as a whole. Others apply to the + * particular policy or the particular decision. + */ +public class PolicyDecisionLogger { + private static final Log log = LogFactory + .getLog(PolicyDecisionLogger.class); + + private static final Pattern NEVER_MATCHES = Pattern.compile("^__NEVER__$"); + + private static final BasicPolicyDecision NULL_DECISION = new BasicPolicyDecision( + INCONCLUSIVE, "The decision was null."); + + private final DeveloperSettings settings; + private final RequestedAction whatToAuth; + private final IdentifierBundle whoToAuth; + + private final boolean enabled; + + private final Pattern policyRestriction; + private final boolean skipInconclusive; + private final boolean includeIdentifiers; + + public PolicyDecisionLogger(IdentifierBundle whoToAuth, + RequestedAction whatToAuth) { + this.settings = DeveloperSettings.getInstance(); + this.whoToAuth = whoToAuth; + this.whatToAuth = whatToAuth; + + this.enabled = figureEnabled(); + + this.policyRestriction = figurePolicyRestriction(); + this.skipInconclusive = figureSkipInconclusive(); + this.includeIdentifiers = figureIncludeIdentifiers(); + } + + private boolean figureEnabled() { + return log.isInfoEnabled() + && settings.getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_ENABLE) + && passesUserRestriction() && passesActionRestriction(); + } + + /** + * The identifier bundle passes if there is no restriction, or if the + * restriction pattern is found within concatenated string of the identifier + * bundle. + * + * If the restriction is invalid, the action fails. + */ + private boolean passesUserRestriction() { + Pattern userRestriction = compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_USER_RESTRICTION); + return userRestriction == null + || userRestriction.matcher(String.valueOf(whoToAuth)).find(); + } + + /** + * The requested action passes if there is no restriction, or if the + * restriction pattern is found within the class name of the action. + * + * If the restriction is invalid, the action fails. + */ + private boolean passesActionRestriction() { + Pattern actionRestriction = compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_ACTION_RESTRICTION); + return actionRestriction == null + || actionRestriction.matcher(String.valueOf(whatToAuth)).find(); + } + + /** + * Only compile the policy restriction pattern once. + */ + private Pattern figurePolicyRestriction() { + return compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_POLICY_RESTRICTION); + } + + /** + * Do we log inconclusive decisions? + */ + private boolean figureSkipInconclusive() { + return settings + .getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_SKIP_INCONCLUSIVE); + } + + /** + * Do we include Identifiers in the log record? + */ + private boolean figureIncludeIdentifiers() { + return settings + .getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_ADD_IDENTIFERS); + } + + /** + * If no pattern was provided, return null. If an invalid pattern was + * provided, return a pattern that never matches. + */ + private Pattern compilePatternFromSetting(Key key) { + String setting = settings.getString(key); + if (setting.isEmpty()) { + return null; + } else { + try { + return Pattern.compile(setting); + } catch (Exception e) { + return NEVER_MATCHES; + } + } + } + + /** + * If the logger and the policy and the decision all pass the restrictions, + * write to the log. A null decision is treated as inconclusive. + */ + public void log(PolicyIface policy, PolicyDecision pd) { + if (passesRestrictions(String.valueOf(policy), pd)) { + if (this.includeIdentifiers) { + log.info(String.format( + "Decision on %s by %s was %s; user is %s", + this.whatToAuth, policy, pd, this.whoToAuth)); + } else { + log.info(String.format("Decision on %s by %s was %s", + this.whatToAuth, policy, pd)); + } + } + } + + private boolean passesRestrictions(String policyString, PolicyDecision pd) { + if (pd == null) { + pd = NULL_DECISION; + } + return enabled && passesPolicyRestriction(policyString) + && passesConclusiveRestriction(pd); + } + + private boolean passesPolicyRestriction(String policyString) { + return this.policyRestriction == null + || this.policyRestriction.matcher(policyString).find(); + } + + private boolean passesConclusiveRestriction(PolicyDecision pd) { + return !(skipInconclusive && isInconclusive(pd)); + } + + private boolean isInconclusive(PolicyDecision pd) { + return pd == null || pd.getAuthorized() == INCONCLUSIVE; + } + + public void logNoDecision(PolicyDecision pd) { + if (enabled) { + if (this.includeIdentifiers) { + log.info(pd.getMessage() + "; user is " + this.whoToAuth); + } else { + log.info(pd.getMessage()); + } + } + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyList.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyList.java index ca6e047ff..8c09034f0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyList.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyList.java @@ -103,14 +103,16 @@ public class PolicyList extends ArrayList implements PolicyIface{ protected PolicyDecision checkAgainstPolicys( IdentifierBundle whoToAuth, RequestedAction whatToAuth){ PolicyDecision pd = null; + PolicyDecisionLogger logger = new PolicyDecisionLogger(whoToAuth, whatToAuth); for(PolicyIface policy : this){ try{ pd = policy.isAuthorized(whoToAuth, whatToAuth); + logger.log(policy, pd); if( pd != null ){ if( pd.getAuthorized() == Authorization.AUTHORIZED ) - break; + return pd; if( pd.getAuthorized() == Authorization.UNAUTHORIZED ) - break; + return pd; if( pd.getAuthorized() == Authorization.INCONCLUSIVE ) continue; } else{ @@ -120,8 +122,11 @@ public class PolicyList extends ArrayList implements PolicyIface{ log.error("ignoring exception in policy " + policy.toString(), th ); } } - log.debug("decision " + pd + " for " + whatToAuth); - return pd; + + pd = new BasicPolicyDecision(Authorization.INCONCLUSIVE, + "No policy returned a conclusive decision on " + whatToAuth); + logger.logNoDecision(pd); + return pd; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java index dfc6727a5..e5ce43bf9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java @@ -205,6 +205,7 @@ public class DeveloperSettings { Properties dsProps = new Properties(); dsProps.load(reader); devSettings.updateFromProperties(dsProps); + log.info(devSettings); ss.info(this, "Loaded the 'developer.properties' file: " + devSettings); } catch (FileNotFoundException e) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java index d519f6b69..fed8dabd1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/Key.java @@ -24,7 +24,7 @@ public enum Key { ENABLED("developer.enabled", true), /** - * If the developer panel is enabled, can a non-logged-in user change the + * If the developer panel is enabled, may an anonymous user change the * settings? */ PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true), @@ -84,7 +84,45 @@ public enum Key { * Tell the ShortViewLogger to note the use of non-default short views. */ PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW( - "developer.pageContents.logCustomShortView", true); + "developer.pageContents.logCustomShortView", true), + + /** + * Enable the PolicyDecisionLogger. + */ + AUTHORIZATION_LOG_DECISIONS_ENABLE( + "developer.authorization.logDecisions.enable", true), + + /** + * Enable the PolicyDecisionLogger. + */ + AUTHORIZATION_LOG_DECISIONS_ADD_IDENTIFERS( + "developer.authorization.logDecisions.addIdentifiers", true), + + /** + * Enable the PolicyDecisionLogger. + */ + AUTHORIZATION_LOG_DECISIONS_SKIP_INCONCLUSIVE( + "developer.authorization.logDecisions.skipInconclusive", true), + + /** + * Don't log policy decisions unless the requested action meets this + * restriction. + */ + AUTHORIZATION_LOG_DECISIONS_ACTION_RESTRICTION( + "developer.authorization.logDecisions.actionRestriction", false), + + /** + * Don't log policy decisions unless the identifier bundle meets this + * restriction. + */ + AUTHORIZATION_LOG_DECISIONS_USER_RESTRICTION( + "developer.authorization.logDecisions.userRestriction", false), + + /** + * Don't log policy decisions unless the policy meets this restriction. + */ + AUTHORIZATION_LOG_DECISIONS_POLICY_RESTRICTION( + "developer.authorization.logDecisions.policyRestriction", false); private static final Log log = LogFactory.getLog(Key.class); private final String propertyName; diff --git a/webapp/web/css/developer/developerPanel.css b/webapp/web/css/developer/developerPanel.css index b93ca7288..8f28000cb 100644 --- a/webapp/web/css/developer/developerPanel.css +++ b/webapp/web/css/developer/developerPanel.css @@ -10,6 +10,9 @@ div.developer { div.developer #developerPanelBody { display: none; +} + +#developerPanelBody * { line-height: 1em; font-size: small; } diff --git a/webapp/web/js/developer/developerPanel.js b/webapp/web/js/developer/developerPanel.js index 684b96dc0..bf14183f9 100644 --- a/webapp/web/js/developer/developerPanel.js +++ b/webapp/web/js/developer/developerPanel.js @@ -48,11 +48,19 @@ function DeveloperPanel(developerAjaxUrl) { document.getElementById("developer_i18n_defeatCache").disabled = !developerEnabled; document.getElementById("developer_i18n_logStringRequests").disabled = !developerEnabled; document.getElementById("developer_loggingRDFService_enable").disabled = !developerEnabled; + document.getElementById("developer_authorization_logDecisions_enable").disabled = !developerEnabled; var rdfServiceEnabled = developerEnabled && document.getElementById("developer_loggingRDFService_enable").checked; document.getElementById("developer_loggingRDFService_stackTrace").disabled = !rdfServiceEnabled; document.getElementById("developer_loggingRDFService_queryRestriction").disabled = !rdfServiceEnabled; document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled; + + var authLoggingEnabled = developerEnabled && document.getElementById("developer_authorization_logDecisions_enable").checked; + document.getElementById("developer_authorization_logDecisions_skipInconclusive").disabled = !authLoggingEnabled; + document.getElementById("developer_authorization_logDecisions_addIdentifiers").disabled = !authLoggingEnabled; + document.getElementById("developer_authorization_logDecisions_actionRestriction").disabled = !authLoggingEnabled; + document.getElementById("developer_authorization_logDecisions_policyRestriction").disabled = !authLoggingEnabled; + document.getElementById("developer_authorization_logDecisions_userRestriction").disabled = !authLoggingEnabled; } function collectFormData() { diff --git a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl index 013d15973..ae2d05fe0 100644 --- a/webapp/web/templates/freemarker/page/partials/developerPanel.ftl +++ b/webapp/web/templates/freemarker/page/partials/developerPanel.ftl @@ -1,12 +1,18 @@ <#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> -<#macro showCheckbox key> - checked> +<#macro showCheckbox key, labelText> + -<#macro showTextbox key> - +<#macro showTextbox key, labelText> + <#if !settings.developer_enabled> @@ -21,14 +27,10 @@
- - + <@showCheckbox "developer_enabled", + "Enable developer mode" /> + <@showCheckbox "developer_permitAnonymousControl", + "Allow anonymous user to see and modify developer settings" />
@@ -41,26 +43,18 @@
Page configuration - - + <@showCheckbox "developer_pageContents_logCustomListView", + "Log the use of custom list view XML files." /> + <@showCheckbox "developer_pageContents_logCustomShortView" , + "Log the use of custom short views in search, index and browse pages."/>
Language support - - + <@showCheckbox "developer_i18n_defeatCache", + "Defeat the cache of language property files" /> + <@showCheckbox "developer_i18n_logStringRequests", + "Log the retrieval of language strings" />
@@ -77,43 +71,43 @@
Freemarker templates - - + <@showCheckbox "developer_defeatFreemarkerCache", + "Defeat the template cache" /> + <@showCheckbox "developer_insertFreemarkerDelimiters", + "Insert HTML comments at start and end of templates" />
SPARQL Queries - + <@showCheckbox "developer_loggingRDFService_enable", + "Log each query" />
- - - + <@showCheckbox "developer_loggingRDFService_stackTrace", + "Add stack trace" /> + <@showTextbox "developer_loggingRDFService_queryRestriction", + "Restrict by query string" /> + <@showTextbox "developer_loggingRDFService_stackRestriction", + "Restrict by calling stack" />
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + <@showCheckbox "developer_authorization_logDecisions_enable", + "Write policy decisions to the log" /> +
+ <@showCheckbox "developer_authorization_logDecisions_skipInconclusive", + "Skip inconclusive decisions" /> + <@showCheckbox "developer_authorization_logDecisions_addIdentifiers", + "Include the user identifiers in the log record" /> + <@showTextbox "developer_authorization_logDecisions_actionRestriction", + "Restrict by requested action" /> + <@showTextbox "developer_authorization_logDecisions_policyRestriction", + "Restrict by policy name" /> + <@showTextbox "developer_authorization_logDecisions_userRestriction", + "Restrict by user identifiers" /> +
From 88fbafc98bf42917af508cd33c3bfa757f52192a Mon Sep 17 00:00:00 2001 From: tworrall Date: Tue, 7 Jan 2014 16:15:27 -0500 Subject: [PATCH 16/31] VIVO-653: implemented suggested fix --- .../body/partials/individual/individual-properties.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl b/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl index 1e28b5c23..4a7d6e7b6 100644 --- a/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl +++ b/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl @@ -12,7 +12,7 @@
<#-- Property display name --> - <#if rangeClass == "Authorship" && individual.editable && property.domainUri?contains("Person")> + <#if rangeClass == "Authorship" && individual.editable && (property.domainUri)?? && property.domainUri?contains("Person")>

${property.name} <@p.addLink property editable /> <@p.verboseDisplay property /> style="padding-top:10px" > ${i18n().manage_publications_link} From 0280cda8e3da014129ad3fa55797efa17201ffd1 Mon Sep 17 00:00:00 2001 From: j2blake Date: Thu, 9 Jan 2014 17:27:14 -0500 Subject: [PATCH 17/31] VIVO-662 Remove duplicate rows from the SPARQL query results. Two rows are considered to be duplicates if they have the same authorshipURI or the same authorURI. --- .../vitro/webapp/dao/jena/QueryUtils.java | 37 ++++++ .../vitro/webapp/dao/jena/QueryUtilsTest.java | 117 +++++++++++++++++- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java index c056a28ec..1c80ec087 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java @@ -2,10 +2,14 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; +import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -56,6 +60,39 @@ public class QueryUtils { return map; } + /** + * If any pair of maps in the list has the same (non-null) value for any of + * these keys, call the maps duplicates and keep only the first of them. + */ + public static List> removeDuplicatesMapsFromList( + List> rawList, String... keys) { + List> filteredList = new ArrayList<>(); + outerLoop: for (Map rawMap : rawList) { + for (Map filteredMap : filteredList) { + for (String key : keys) { + String rawValue = rawMap.get(key); + if (rawValue != null) { + if (rawValue.equals(filteredMap.get(key))) { + if (log.isDebugEnabled()) { + logDuplicateRows(rawMap, filteredMap, keys); + } + continue outerLoop; + } + } + } + } + filteredList.add(rawMap); + } + return filteredList; + } + + private static void logDuplicateRows(Map rawMap, + Map filteredMap, String... keys) { + log.debug("Found duplicate rows, by at least one of these keys: " + + Arrays.toString(keys) + ". Keeping " + filteredMap + + ". Discarding " + rawMap + "."); + } + public static Object nodeToObject( RDFNode node ){ if( node == null ){ return ""; diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtilsTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtilsTest.java index 837c5cf50..aa8e24020 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtilsTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtilsTest.java @@ -3,6 +3,14 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.Ignore; import org.junit.Test; @@ -18,6 +26,11 @@ import edu.cornell.mannlib.vitro.testing.AbstractTestClass; * TODO */ public class QueryUtilsTest extends AbstractTestClass { + + // ---------------------------------------------------------------------- + // Test bindVariables + // ---------------------------------------------------------------------- + private QuerySolutionMap bindings = new QuerySolutionMap(); @Test @@ -54,12 +67,112 @@ public class QueryUtilsTest extends AbstractTestClass { fail("bindAnon not implemented"); } - // ---------------------------------------------------------------------- - // Helper methods // ---------------------------------------------------------------------- private void assertBoundQueryEquals(String template, String expected) { String actual = QueryUtils.bindVariables(template, bindings); assertEquals("bounding results", expected, actual); } + + // ---------------------------------------------------------------------- + // Test removeDuplicatesMapsFromList + // ---------------------------------------------------------------------- + + private List> theList = list( + map(pair("id", "1"), pair("color", "blue"), pair("size", "large")), + map(pair("id", "2"), pair("color", "red"), pair("size", "large"), + pair("parity", "odd"))); + private List> filteredList; + + @Test + public void noKeys() { + assertExpectedIDs(ids("1", "2"), keys()); + } + + @Test + public void emptyList() { + theList = new ArrayList<>(); + assertExpectedIDs(ids(), keys("color")); + } + + @Test + public void unrecognizedKey() { + assertExpectedIDs(ids("1", "2"), keys("bogus")); + } + + @Test + public void unmatchedKey() { + assertExpectedIDs(ids("1", "2"), keys("parity")); + } + + @Test + public void foundDuplicate() { + assertExpectedIDs(ids("1"), keys("size")); + } + + @Test + public void noDuplicates() { + assertExpectedIDs(ids("1", "2"), keys("color")); + } + + @Test + public void matchOneKeyOfMany() { + assertExpectedIDs(ids("1"), keys("color", "size")); + } + + @Test + public void multipleDuplicatesOfASingleRecord() { + theList.add(map(pair("id", "3"), pair("size", "large"))); + assertExpectedIDs(ids("1"), keys("color", "size")); + } + + // ---------------------------------------------------------------------- + + private void assertExpectedIDs(String[] ids, String[] keys) { + filteredList = QueryUtils.removeDuplicatesMapsFromList(theList, keys); + assertEquals("ids", Arrays.asList(ids), idsInFilteredList()); + } + + private List idsInFilteredList() { + List ids = new ArrayList<>(); + for (Map map : filteredList) { + String id = map.get("id"); + if (id == null) { + fail("ID was null"); + } else { + ids.add(id); + } + } + return ids; + } + + @SafeVarargs + private final List> list(Map... maps) { + return new ArrayList<>(Arrays.asList(maps)); + } + + private Map map(String[]... pairs) { + Map map = new HashMap<>(); + for (String[] pair : pairs) { + map.put(pair[0], pair[1]); + } + return map; + } + + private String[] pair(String... s) { + return s; + } + + private String[] keys(String... keys) { + return keys; + } + + private String[] ids(String... ids) { + return ids; + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + } From 2f68ddb8ee63fb6ff10f4567fad0af6332e9e63a Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 15 Jan 2014 15:16:17 -0500 Subject: [PATCH 18/31] VIVO-672 Preserve the lazy initialization of the VClassJena. Instead of a copy constructor on VClass, use a copy() method, which can be overridden. --- .../mannlib/vitro/webapp/beans/VClass.java | 42 +++++++++++-------- .../webapp/dao/VClassGroupsForRequest.java | 6 +-- .../vitro/webapp/dao/jena/VClassJena.java | 10 +++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/VClass.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/VClass.java index 128161cb5..2a6b0844b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/VClass.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/VClass.java @@ -137,26 +137,32 @@ public class VClass extends BaseResourceBean implements Comparable /** * Constructs the VClass as a deep copy of an existing VClass. */ - public VClass( VClass vc) { - this.URI = vc.URI; - this.namespace = vc.namespace; - this.localName = vc.localName; - this.myName = vc.myName; - this.myExample = vc.myExample; - this.myDescription = vc.myDescription; - this.myShortDefinition = vc.myShortDefinition; - this.myEntityCount = vc.myEntityCount; - this.displayLimit = vc.displayLimit; - this.displayRank = vc.displayRank; - this.quickEditJsp = vc.quickEditJsp; - this.groupURI = vc.groupURI; - this.group = (vc.group == null) ? null : new VClassGroup(vc.group); - this.customEntryForm = vc.customEntryForm; - this.customDisplayView = vc.customDisplayView; - this.customShortView = vc.customShortView; - this.customSearchView = vc.customSearchView; + public VClass copy() { + VClass that = new VClass(); + copyFields(that); + return that; } + protected void copyFields(VClass that) { + that.myName = this.myName; + that.namespace = this.namespace; + that.localName = this.localName; + that.URI = this.URI; + that.myExample = this.myExample; + that.myDescription = this.myDescription; + that.myShortDefinition = this.myShortDefinition; + that.myEntityCount = this.myEntityCount; + that.displayLimit = this.displayLimit; + that.displayRank = this.displayRank; + that.quickEditJsp = this.quickEditJsp; + that.groupURI = this.groupURI; + that.group = this.group; + that.customEntryForm = this.customEntryForm; + that.customDisplayView = this.customDisplayView; + that.customShortView = this.customShortView; + that.customSearchView = this.customSearchView; + } + /** * Sorts alphabetically by name */ diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VClassGroupsForRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VClassGroupsForRequest.java index 753e695e0..20484b00b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VClassGroupsForRequest.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VClassGroupsForRequest.java @@ -37,8 +37,8 @@ public class VClassGroupsForRequest { } if (log.isDebugEnabled()) { - log.debug("groups: " + groupMap); - log.debug("classes: " + classMap); + log.debug("groups: " + groupMap.values()); + log.debug("classes: " + classMap.values()); } } @@ -59,7 +59,7 @@ public class VClassGroupsForRequest { } private void loadClass(VClass vc, VClassGroup newVcg) { - VClass newVc = new VClass(vc); + VClass newVc = vc.copy(); newVc.setName(getNameForVClass(vc)); newVc.setGroup(newVcg); newVcg.add(newVc); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJena.java index cd62ab469..5f6b9aee3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassJena.java @@ -47,6 +47,16 @@ public class VClassJena extends VClass { this.webappDaoFactory = wadf; } + /** + * Constructs the VClassJena as a deep copy of an existing VClassJena. + */ + @Override + public VClassJena copy() { + VClassJena that = new VClassJena(this.cls, this.webappDaoFactory); + copyFields(that); + return that; + } + /** * What this VClass is called */ From 73f760492004dcf9899c7da857e40fdc5fd9a67d Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 15 Jan 2014 15:29:08 -0500 Subject: [PATCH 19/31] VIVO-673 Log throwables and re-throw them. If a startup module throws an error, StartupManager will catch it only long enough to write it to the log, and then re-throw it. --- .../mannlib/vitro/webapp/startup/StartupManager.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupManager.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupManager.java index a3a6f76e2..20bf0f34c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupManager.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupManager.java @@ -69,6 +69,9 @@ public class StartupManager implements ServletContextListener { } catch (Exception e) { ss.fatal(this, "Startup threw an unexpected exception.", e); log.error("Startup threw an unexpected exception.", e); + } catch (Throwable t) { + log.fatal("Startup threw an unexpected error.", t); + throw t; } } @@ -177,6 +180,9 @@ public class StartupManager implements ServletContextListener { ss.listenerExecuted(listener); } catch (Exception e) { ss.fatal(listener, "Threw unexpected exception", e); + } catch (Throwable t) { + log.fatal(listener + " Threw unexpected error", t); + throw t; } } @@ -214,6 +220,10 @@ public class StartupManager implements ServletContextListener { } catch (Exception e) { log.error("Unexpected exception from contextDestroyed() on '" + listener.getClass().getName() + "'", e); + } catch (Throwable t) { + log.fatal("Unexpected error from contextDestroyed() on '" + + listener.getClass().getName() + "'", t); + throw t; } } log.info("Called 'contextDestroyed' on all listeners."); From 95538e4c8a5d09628323f9b2563d6356c2ae0483 Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 15 Jan 2014 16:43:40 -0500 Subject: [PATCH 20/31] VIVO-230 Make the log messages more explicit, so we have a chance of diagnosing this. --- .../vitro/webapp/controller/json/JsonObjectProducer.java | 2 +- .../mannlib/vitro/webapp/controller/json/JsonProducer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonObjectProducer.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonObjectProducer.java index 2af1b6b83..f69589ecf 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonObjectProducer.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonObjectProducer.java @@ -51,7 +51,7 @@ public abstract class JsonObjectProducer extends JsonProducer { try { jsonObject = process(); } catch (Exception e) { - log.error("Failed to create JSON response" + e); + log.error("Failed to create JSON response", e); errorMessage = e.toString(); resp.setStatus(500 /* HttpURLConnection.HTTP_SERVER_ERROR */); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java index 86ce2da05..a1a5e2c0d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java @@ -80,7 +80,7 @@ public abstract class JsonProducer { VClass vclass = vreq.getWebappDaoFactory().getVClassDao() .getVClassByURI(uri); if (vclass == null) { - log.error("Couldn't retrieve vclass "); + log.error("Couldn't retrieve vclass '" + uri + "'"); throw new IllegalStateException("Class " + uri + " not found"); } return vclass; From fdeee35bb4b615a348111ab03cda83268b160384 Mon Sep 17 00:00:00 2001 From: j2blake Date: Mon, 10 Mar 2014 17:42:05 -0400 Subject: [PATCH 21/31] VIVO-692 Restrict LOD by Publish level, not by Display level Create a new annotation for properties and classes, HiddenFromPublishBelowRoleLevelAnnot. Provide the means to initialize these annotations, edit them, and display them in the verbose property display. Create a Permission and some requested actions so the policies can decide which statements must be filtered out, based on the user's role. Add unit tests and improve acceptance tests --- webapp/config/log4j.properties | 1 - .../rdf/auth/everytime/permission_config.n3 | 6 + webapp/rdf/tbox/filegraph/vitro-0.7.owl | 3 + .../permissions/DisplayByRolePermission.java | 1 - .../auth/permissions/PermissionRegistry.java | 20 + .../permissions/PublishByRolePermission.java | 133 +++++ .../webapp/auth/policy/ServletPolicyList.java | 2 - .../bean/PropertyRestrictionListener.java | 26 +- .../bean/PropertyRestrictionPolicyHelper.java | 83 ++- .../AbstractDataPropertyStatementAction.java | 4 + ...AbstractObjectPropertyStatementAction.java | 26 +- .../publish/PublishDataProperty.java | 24 + .../publish/PublishDataPropertyStatement.java | 26 + .../publish/PublishObjectProperty.java | 24 + .../PublishObjectPropertyStatement.java | 28 + .../vitro/webapp/beans/BaseResourceBean.java | 198 +++---- .../vitro/webapp/beans/ObjectProperty.java | 1 + .../vitro/webapp/beans/ResourceBean.java | 6 + .../edit/DatapropEditController.java | 11 +- .../edit/DatapropRetryController.java | 1 + .../controller/edit/EntityEditController.java | 10 +- .../edit/EntityRetryController.java | 1 + .../edit/PropertyEditController.java | 15 +- .../edit/PropertyRetryController.java | 1 + .../controller/edit/VclassEditController.java | 13 +- .../edit/VclassRetryController.java | 1 + .../edit/utils/RoleLevelOptionsSetup.java | 33 +- .../individual/IndividualRdfAssembler.java | 34 +- .../vitro/webapp/dao/VitroVocabulary.java | 6 +- .../dao/filtering/DataPropertyFiltering.java | 15 + .../dao/filtering/IndividualFiltering.java | 15 + .../filtering/ObjectPropertyFiltering.java | 15 + .../webapp/dao/jena/DataPropertyDaoJena.java | 28 + .../vitro/webapp/dao/jena/JenaBaseDaoCon.java | 1 + .../vitro/webapp/dao/jena/JenaModelUtils.java | 4 +- .../dao/jena/ObjectPropertyDaoJena.java | 32 +- .../vitro/webapp/dao/jena/VClassDaoJena.java | 13 + .../vitro/webapp/dao/jena/VClassJena.java | 37 +- .../individual/PropertyTemplateModel.java | 8 +- .../vitro/testing/AbstractTestClass.java | 21 + .../PropertyRestrictionPolicyHelperTest.java | 167 ++++-- .../IndividualRdfAssemblerTest.filteredRdf.n3 | 154 ++++++ .../IndividualRdfAssemblerTest.java | 503 ++++++++++++++++++ .../IndividualRdfAssemblerTest.rawRdf.n3 | 163 ++++++ ...ndividualRdfAssemblerTest.unfilteredRdf.n3 | 171 ++++++ .../dao/jena/DataPropertyDaoJenaTest.java | 1 + .../dao/jena/ObjectPropertyDaoJenaTest.java | 1 + .../vitro/webapp/dao/jena/VClassDaoTest.java | 1 + .../vitro/webapp/dao/jena/VClassJenaTest.java | 22 + .../PropertyRestrictionPolicyHelperStub.java | 27 +- .../vitro/webapp/beans/IndividualStub.java | 34 +- .../edit/specific/dataprop_retry.jsp | 18 +- .../edit/specific/property_retry.jsp | 18 +- .../templates/edit/specific/vclass_retry.jsp | 18 +- .../freemarker/lib/lib-properties.ftl | 3 +- 55 files changed, 1977 insertions(+), 251 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PublishByRolePermission.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataProperty.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataPropertyStatement.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectProperty.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectPropertyStatement.java create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssemblerTest.filteredRdf.n3 create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssemblerTest.java create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssemblerTest.rawRdf.n3 create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssemblerTest.unfilteredRdf.n3 diff --git a/webapp/config/log4j.properties b/webapp/config/log4j.properties index b5cf2bf83..fbb00c755 100644 --- a/webapp/config/log4j.properties +++ b/webapp/config/log4j.properties @@ -35,7 +35,6 @@ log4j.rootLogger=INFO, AllAppender # These classes are too chatty to display INFO messages. log4j.logger.edu.cornell.mannlib.vitro.webapp.startup.StartupStatus=WARN -log4j.logger.edu.cornell.mannlib.vitro.webapp.controller.freemarker.BrowseController=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.dao.jena.RDBGraphGenerator=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase=DEBUG diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3 index 3af62fb71..dfb33c3c7 100644 --- a/webapp/rdf/auth/everytime/permission_config.n3 +++ b/webapp/rdf/auth/everytime/permission_config.n3 @@ -5,6 +5,7 @@ @prefix simplePermission: . @prefix displayByRole: . @prefix editByRole: . +@prefix publishByRole: . auth:ADMIN a auth:PermissionSet ; @@ -65,6 +66,7 @@ auth:ADMIN # role-based permissions for ADMIN auth:hasPermission displayByRole:Admin ; auth:hasPermission editByRole:Admin ; + auth:hasPermission publishByRole:Admin ; . auth:CURATOR @@ -104,6 +106,7 @@ auth:CURATOR # role-based permissions for CURATOR auth:hasPermission displayByRole:Curator ; auth:hasPermission editByRole:Curator ; + auth:hasPermission publishByRole:Curator ; . auth:EDITOR @@ -134,6 +137,7 @@ auth:EDITOR # role-based permissions for EDITOR auth:hasPermission displayByRole:Editor ; auth:hasPermission editByRole:Editor ; + auth:hasPermission publishByRole:Editor ; . auth:SELF_EDITOR @@ -158,6 +162,7 @@ auth:SELF_EDITOR # For role-based display and editing, SelfEditor is like Public. # SelfEditor uses its special permissions to edit/display its own values. auth:hasPermission displayByRole:Public ; + auth:hasPermission publishByRole:Public ; . auth:PUBLIC @@ -171,4 +176,5 @@ auth:PUBLIC # role-based permissions for PUBLIC auth:hasPermission displayByRole:Public ; + auth:hasPermission publishByRole:Public ; . diff --git a/webapp/rdf/tbox/filegraph/vitro-0.7.owl b/webapp/rdf/tbox/filegraph/vitro-0.7.owl index a265a3508..6e9b89da6 100644 --- a/webapp/rdf/tbox/filegraph/vitro-0.7.owl +++ b/webapp/rdf/tbox/filegraph/vitro-0.7.owl @@ -109,6 +109,9 @@ xmlns:vitro="&vitro;" + + + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java index 74ff5c584..3486de761 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java @@ -15,7 +15,6 @@ import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayObje import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; -import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.Property; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java index 775ace11b..ae9af0073 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java @@ -134,6 +134,7 @@ public class PermissionRegistry { permissions.addAll(SimplePermission.getAllInstances()); permissions.addAll(createDisplayByRolePermissions(ctx)); permissions.addAll(createEditByRolePermissions(ctx)); + permissions.addAll(createPublishByRolePermissions(ctx)); PermissionRegistry.createRegistry(ctx, permissions); @@ -185,5 +186,24 @@ public class PermissionRegistry { public void contextDestroyed(ServletContextEvent sce) { sce.getServletContext().removeAttribute(ATTRIBUTE_NAME); } + + /** + * There is no PublishByRolePermission for self-editors. They get the + * same rights as PUBLIC. Other permissions give them their self-editing + * privileges. + */ + private Collection createPublishByRolePermissions( + ServletContext ctx) { + List list = new ArrayList(); + list.add(new PublishByRolePermission("Admin", RoleLevel.DB_ADMIN, + ctx)); + list.add(new PublishByRolePermission("Curator", RoleLevel.CURATOR, + ctx)); + list.add(new PublishByRolePermission("Editor", RoleLevel.EDITOR, + ctx)); + list.add(new PublishByRolePermission("Public", RoleLevel.PUBLIC, + ctx)); + return list; + } } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PublishByRolePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PublishByRolePermission.java new file mode 100644 index 000000000..c70be7735 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PublishByRolePermission.java @@ -0,0 +1,133 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.permissions; + +import javax.servlet.ServletContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.auth.policy.bean.PropertyRestrictionPolicyHelper; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish.PublishDataProperty; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish.PublishDataPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish.PublishObjectProperty; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish.PublishObjectPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel; +import edu.cornell.mannlib.vitro.webapp.beans.Property; + +/** + * Is the user authorized to publish properties that are marked as restricted to + * a certain "Role Level"? + */ +public class PublishByRolePermission extends Permission { + private static final Log log = LogFactory + .getLog(PublishByRolePermission.class); + + public static final String NAMESPACE = "java:" + + PublishByRolePermission.class.getName() + "#"; + + private final String roleName; + private final RoleLevel roleLevel; + private final ServletContext ctx; + + public PublishByRolePermission(String roleName, RoleLevel roleLevel, + ServletContext ctx) { + super(NAMESPACE + roleName); + + if (roleName == null) { + throw new NullPointerException("role may not be null."); + } + if (roleLevel == null) { + throw new NullPointerException("roleLevel may not be null."); + } + if (ctx == null) { + throw new NullPointerException("context may not be null."); + } + + this.roleName = roleName; + this.roleLevel = roleLevel; + this.ctx = ctx; + } + + @Override + public boolean isAuthorized(RequestedAction whatToAuth) { + boolean result; + + if (whatToAuth instanceof PublishDataProperty) { + result = isAuthorized((PublishDataProperty) whatToAuth); + } else if (whatToAuth instanceof PublishObjectProperty) { + result = isAuthorized((PublishObjectProperty) whatToAuth); + } else if (whatToAuth instanceof PublishDataPropertyStatement) { + result = isAuthorized((PublishDataPropertyStatement) whatToAuth); + } else if (whatToAuth instanceof PublishObjectPropertyStatement) { + result = isAuthorized((PublishObjectPropertyStatement) whatToAuth); + } else { + result = false; + } + + if (result) { + log.debug(this + " authorizes " + whatToAuth); + } else { + log.debug(this + " does not authorize " + whatToAuth); + } + + return result; + } + + /** + * The user may publish this data property if they are allowed to publish + * its predicate. + */ + private boolean isAuthorized(PublishDataProperty action) { + String predicateUri = action.getDataProperty().getURI(); + return canPublishPredicate(new Property(predicateUri)); + } + + /** + * The user may publish this object property if they are allowed to publish + * its predicate. + */ + private boolean isAuthorized(PublishObjectProperty action) { + return canPublishPredicate(action.getObjectProperty()); + } + + /** + * The user may publish this data property if they are allowed to publish + * its subject and its predicate. + */ + private boolean isAuthorized(PublishDataPropertyStatement action) { + String subjectUri = action.getSubjectUri(); + String predicateUri = action.getPredicateUri(); + return canPublishResource(subjectUri) + && canPublishPredicate(new Property(predicateUri)); + } + + /** + * The user may publish this data property if they are allowed to publish + * its subject, its predicate, and its object. + */ + private boolean isAuthorized(PublishObjectPropertyStatement action) { + String subjectUri = action.getSubjectUri(); + Property predicate = action.getPredicate(); + String objectUri = action.getObjectUri(); + return canPublishResource(subjectUri) && canPublishPredicate(predicate) + && canPublishResource(objectUri); + } + + private boolean canPublishResource(String resourceUri) { + return PropertyRestrictionPolicyHelper.getBean(ctx).canPublishResource( + resourceUri, this.roleLevel); + } + + private boolean canPublishPredicate(Property predicate) { + return PropertyRestrictionPolicyHelper.getBean(ctx) + .canPublishPredicate(predicate, this.roleLevel); + } + + @Override + public String toString() { + return "PublishByRolePermission['" + roleName + "']"; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/ServletPolicyList.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/ServletPolicyList.java index 20b3816dc..47ebbe932 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/ServletPolicyList.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/ServletPolicyList.java @@ -48,7 +48,6 @@ public class ServletPolicyList { PolicyList policies = getPolicyList(sc); if (!policies.contains(policy)) { policies.add(policy); - log.info("Added policy: " + policy.getClass().getSimpleName()); log.debug("Added policy: " + policy.toString()); } else { log.warn("Ignored attempt to add redundant policy."); @@ -67,7 +66,6 @@ public class ServletPolicyList { PolicyList policies = getPolicyList(sc); if (!policies.contains(policy)) { policies.add(0, policy); - log.info("Added policy at front: " + policy.getClass().getSimpleName()); log.debug("Added policy at front: " + policy.toString()); } else { log.warn("Ignored attempt to add redundant policy."); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionListener.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionListener.java index 90f9b630a..e61d798db 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionListener.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionListener.java @@ -37,8 +37,9 @@ public class PropertyRestrictionListener implements ChangeListener { @Override public void doDeleted(Object oldObj, EditProcessObject epo) { Property p = (Property) oldObj; - if (eitherRoleChanged(p.getHiddenFromDisplayBelowRoleLevel(), - p.getProhibitedFromUpdateBelowRoleLevel(), null, null)) { + if (anyRoleChanged(p.getHiddenFromDisplayBelowRoleLevel(), + p.getProhibitedFromUpdateBelowRoleLevel(), + p.getHiddenFromPublishBelowRoleLevel(), null, null, null)) { log.debug("rebuilding the PropertyRestrictionPolicyHelper after deletion"); createAndSetBean(); } @@ -50,9 +51,10 @@ public class PropertyRestrictionListener implements ChangeListener { @Override public void doInserted(Object newObj, EditProcessObject epo) { Property p = (Property) newObj; - if (eitherRoleChanged(null, null, + if (anyRoleChanged(null, null, null, p.getHiddenFromDisplayBelowRoleLevel(), - p.getProhibitedFromUpdateBelowRoleLevel())) { + p.getProhibitedFromUpdateBelowRoleLevel(), + p.getHiddenFromPublishBelowRoleLevel())) { log.debug("rebuilding the PropertyRestrictionPolicyHelper after insertion"); createAndSetBean(); } @@ -65,20 +67,24 @@ public class PropertyRestrictionListener implements ChangeListener { public void doUpdated(Object oldObj, Object newObj, EditProcessObject epo) { Property oldP = (Property) oldObj; Property newP = (Property) newObj; - if (eitherRoleChanged(oldP.getHiddenFromDisplayBelowRoleLevel(), + if (anyRoleChanged(oldP.getHiddenFromDisplayBelowRoleLevel(), oldP.getProhibitedFromUpdateBelowRoleLevel(), + oldP.getHiddenFromPublishBelowRoleLevel(), newP.getHiddenFromDisplayBelowRoleLevel(), - newP.getProhibitedFromUpdateBelowRoleLevel())) { + newP.getProhibitedFromUpdateBelowRoleLevel(), + newP.getHiddenFromPublishBelowRoleLevel())) { log.debug("rebuilding the PropertyRestrictionPolicyHelper after update"); createAndSetBean(); } } - private boolean eitherRoleChanged(RoleLevel oldDisplayRole, - RoleLevel oldUpdateRole, RoleLevel newDisplayRole, - RoleLevel newUpdateRole) { + private boolean anyRoleChanged(RoleLevel oldDisplayRole, + RoleLevel oldUpdateRole, RoleLevel oldPublishRole, + RoleLevel newDisplayRole, RoleLevel newUpdateRole, + RoleLevel newPublishRole) { return (!isTheSame(oldDisplayRole, newDisplayRole)) - || (!isTheSame(oldUpdateRole, newUpdateRole)); + || (!isTheSame(oldUpdateRole, newUpdateRole)) + || (!isTheSame(oldPublishRole, newPublishRole)); } private boolean isTheSame(RoleLevel oldRole, RoleLevel newRole) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionPolicyHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionPolicyHelper.java index e5163efd2..5b4f9ee12 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionPolicyHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/bean/PropertyRestrictionPolicyHelper.java @@ -19,12 +19,6 @@ import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntModelSpec; -import com.hp.hpl.jena.query.Query; -import com.hp.hpl.jena.query.QueryExecution; -import com.hp.hpl.jena.query.QueryExecutionFactory; -import com.hp.hpl.jena.query.QueryFactory; -import com.hp.hpl.jena.query.QuerySolution; -import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.RDFNode; @@ -46,10 +40,10 @@ import edu.cornell.mannlib.vitro.webapp.utils.ApplicationConfigurationOntologyUt /** * Assists the role-based policies in determining whether a property or resource - * may be displayed or modified. + * may be displayed, modified, or published in linked open data. * * There is a bean in the context that holds the current threshold role levels - * for displaying and modifying restricted properties. + * for displaying, modifying, or publishing restricted properties. * * Create this bean after the Jena model is in place in the context. * @@ -119,6 +113,8 @@ public class PropertyRestrictionPolicyHelper { new HashMap>, RoleLevel>(); Map>, RoleLevel> modifyThresholdMap = new HashMap>, RoleLevel>(); + Map>, RoleLevel> publishThresholdMap = + new HashMap>, RoleLevel>(); OntModel union = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, ModelFactory.createUnion(displayModel, model)); @@ -126,15 +122,14 @@ public class PropertyRestrictionPolicyHelper { populateThresholdMap(union, displayThresholdMap, VitroVocabulary.HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT); - populateThresholdMap( - union, - modifyThresholdMap, + populateThresholdMap(union, modifyThresholdMap, VitroVocabulary.PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT); - + populateThresholdMap(union, publishThresholdMap, + VitroVocabulary.HIDDEN_FROM_PUBLISH_BELOW_ROLE_LEVEL_ANNOT); PropertyRestrictionPolicyHelper bean = new PropertyRestrictionPolicyHelper( PROHIBITED_NAMESPACES, PERMITTED_EXCEPTIONS, - displayThresholdMap, modifyThresholdMap, displayModel); + displayThresholdMap, modifyThresholdMap, publishThresholdMap); return bean; } @@ -209,6 +204,9 @@ public class PropertyRestrictionPolicyHelper { } else if (VitroVocabulary.HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT .equals(propertyUri)) { role = faux.getHiddenFromDisplayBelowRoleLevel(); + } else if (VitroVocabulary.HIDDEN_FROM_PUBLISH_BELOW_ROLE_LEVEL_ANNOT + .equals(propertyUri)) { + role = faux.getHiddenFromPublishBelowRoleLevel(); } if (role != null) { log.debug("Putting D:" + faux.getDomainVClassURI() + " P:" + faux.getURI() + " R:" + faux.getRangeVClassURI() + " ==> L:" + role); @@ -251,6 +249,12 @@ public class PropertyRestrictionPolicyHelper { */ private final Map>, RoleLevel> modifyThresholdMap; + /** + * These URIs can be published only if the user's role is at least as high as + * the threshold role. + */ + private final Map>, RoleLevel> publishThresholdMap; + /** * Store unmodifiable versions of the inputs. @@ -263,19 +267,23 @@ public class PropertyRestrictionPolicyHelper { Collection modifyExceptionsAllowedUris, Map>, RoleLevel> displayThresholdMap, Map>, RoleLevel> modifyThresholdMap, - Model displayModel) { + Map>, RoleLevel> publishThresholdMap) { this.modifyProhibitedNamespaces = unmodifiable(modifyProhibitedNamespaces); this.modifyExceptionsAllowedUris = unmodifiable(modifyExceptionsAllowedUris); this.displayThresholdMap = displayThresholdMap; this.modifyThresholdMap = modifyThresholdMap; -// this.displayThresholdMap = unmodifiable(displayThresholdMap); -// this.modifyThresholdMap = unmodifiable(modifyThresholdMap); + this.publishThresholdMap = publishThresholdMap; + // TODO: why are these no longer unmodifiable? Brian changed during the + // TODO: ISF integration. + // this.displayThresholdMap = unmodifiable(displayThresholdMap); + // this.modifyThresholdMap = unmodifiable(modifyThresholdMap); if (log.isDebugEnabled()) { log.debug("prohibited: " + this.modifyProhibitedNamespaces); log.debug("exceptions: " + this.modifyExceptionsAllowedUris); log.debug("display thresholds: " + this.displayThresholdMap); log.debug("modify thresholds: " + this.modifyThresholdMap); + log.debug("publish thresholds: " + this.publishThresholdMap); } } @@ -341,6 +349,22 @@ public class PropertyRestrictionPolicyHelper { return true; } + /** + * Any resource can be published. + * + * (Someday we may want to implement publish restrictions based on VClass.) + */ + @SuppressWarnings("unused") + public boolean canPublishResource(String resourceUri, RoleLevel userRole) { + if (resourceUri == null) { + log.debug("can't publish resource: resourceUri was null"); + return false; + } + + log.debug("can publish resource '" + resourceUri + "'"); + return true; + } + /** * If display of a predicate is restricted, the user's role must be at least * as high as the restriction level. @@ -408,6 +432,33 @@ public class PropertyRestrictionPolicyHelper { return false; } + /** + * If publishing of a predicate is restricted, the user's role must be at least + * as high as the restriction level. + */ + public boolean canPublishPredicate(Property predicate, RoleLevel userRole) { + if (predicate == null) { + log.debug("can't publish predicate: predicate was null"); + return false; + } + + RoleLevel publishThreshold = getThreshold(predicate, publishThresholdMap); + + if (isAuthorized(userRole, publishThreshold)) { + log.debug("can publish predicate: '" + predicate.getURI() + "', domain=" + + predicate.getDomainVClassURI() + ", range=" + + predicate.getRangeVClassURI() + ", userRole=" + + userRole + ", thresholdRole=" + publishThreshold); + return true; + } + + log.debug("can't publish predicate: '" + predicate.getURI() + "', domain=" + + predicate.getDomainVClassURI() + ", range=" + + predicate.getRangeVClassURI() + ", userRole=" + + userRole + ", thresholdRole=" + publishThreshold); + return false; + } + private boolean isAuthorized(RoleLevel userRole, RoleLevel thresholdRole) { if (userRole == null) { return false; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractDataPropertyStatementAction.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractDataPropertyStatementAction.java index 8e49ec193..98fec8b2a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractDataPropertyStatementAction.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractDataPropertyStatementAction.java @@ -59,6 +59,10 @@ public abstract class AbstractDataPropertyStatementAction extends public String[] getResourceUris() { return new String[] {subjectUri}; } + + public String dataValue() { + return dataValue; + } @Override public String toString() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractObjectPropertyStatementAction.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractObjectPropertyStatementAction.java index 700b0bf24..2bc7e3c7c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractObjectPropertyStatementAction.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/propstmt/AbstractObjectPropertyStatementAction.java @@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt; import com.hp.hpl.jena.ontology.OntModel; +import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.Property; @@ -17,23 +18,31 @@ public abstract class AbstractObjectPropertyStatementAction extends private final Property predicate; private final String objectUri; - public AbstractObjectPropertyStatementAction(OntModel ontModel, String subjectUri, - Property predicate, String objectUri) { + public AbstractObjectPropertyStatementAction(OntModel ontModel, + String subjectUri, Property predicate, String objectUri) { super(ontModel); this.subjectUri = subjectUri; this.predicate = predicate; this.objectUri = objectUri; } - public AbstractObjectPropertyStatementAction(OntModel ontModel, ObjectPropertyStatement ops) { + public AbstractObjectPropertyStatementAction(OntModel ontModel, + ObjectPropertyStatement ops) { super(ontModel); this.subjectUri = (ops.getSubject() == null) ? ops.getSubjectURI() : ops.getSubject().getURI(); - this.predicate = (ops.getProperty()); + this.predicate = (ops.getProperty() == null) ? createProperty(ops + .getPropertyURI()) : ops.getProperty(); this.objectUri = (ops.getObject() == null) ? ops.getObjectURI() : ops .getObject().getURI(); } + private ObjectProperty createProperty(String propertyURI) { + ObjectProperty op = new ObjectProperty(); + op.setURI(propertyURI); + return op; + } + public String getSubjectUri() { return subjectUri; } @@ -41,11 +50,12 @@ public abstract class AbstractObjectPropertyStatementAction extends public String getObjectUri() { return objectUri; } - + + @Override public Property getPredicate() { - return predicate; + return predicate; } - + @Override public String getPredicateUri() { return predicate.getURI(); @@ -53,7 +63,7 @@ public abstract class AbstractObjectPropertyStatementAction extends @Override public String[] getResourceUris() { - return new String[] {subjectUri, objectUri}; + return new String[] { subjectUri, objectUri }; } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataProperty.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataProperty.java new file mode 100644 index 000000000..1b6ef3f20 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataProperty.java @@ -0,0 +1,24 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish; + +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; + +/** Should we allow the user to publish this DataProperty in Linked Open Data? */ +public class PublishDataProperty extends RequestedAction { + private final DataProperty dataProperty; + + public PublishDataProperty(DataProperty dataProperty) { + this.dataProperty = dataProperty; + } + + public DataProperty getDataProperty() { + return dataProperty; + } + + @Override + public String toString() { + return "PublishDataProperty[" + dataProperty + "]"; + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataPropertyStatement.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataPropertyStatement.java new file mode 100644 index 000000000..e69a3b618 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishDataPropertyStatement.java @@ -0,0 +1,26 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish; + +import com.hp.hpl.jena.ontology.OntModel; + +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.AbstractDataPropertyStatementAction; +import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; + +/** + * Should we publish this DataPropertyStatement in a Linked Open Data request + * from the current user? + */ +public class PublishDataPropertyStatement extends + AbstractDataPropertyStatementAction { + public PublishDataPropertyStatement(OntModel ontModel, String subjectUri, + String predicateUri, String dataValue) { + super(ontModel, subjectUri, predicateUri, dataValue); + } + + public PublishDataPropertyStatement(OntModel ontModel, + DataPropertyStatement dps) { + super(ontModel, dps); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectProperty.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectProperty.java new file mode 100644 index 000000000..19358278a --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectProperty.java @@ -0,0 +1,24 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish; + +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; + +/** Should we allow the user to publish this ObjectProperty in Linked Open Data? */ +public class PublishObjectProperty extends RequestedAction { + private final ObjectProperty objectProperty; + + public PublishObjectProperty(ObjectProperty objectProperty) { + this.objectProperty = objectProperty; + } + + public ObjectProperty getObjectProperty() { + return objectProperty; + } + + @Override + public String toString() { + return "PublishObjectProperty[" + objectProperty.getLocalName() + "]"; + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectPropertyStatement.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectPropertyStatement.java new file mode 100644 index 000000000..62b8a3113 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/requestedAction/publish/PublishObjectPropertyStatement.java @@ -0,0 +1,28 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.auth.requestedAction.publish; + +import com.hp.hpl.jena.ontology.OntModel; + +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.AbstractObjectPropertyStatementAction; +import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.beans.Property; + +/** + * Should we publish this ObjectPropertyStatement in a Linked Open Data request + * from the current user? + */ + +public class PublishObjectPropertyStatement extends + AbstractObjectPropertyStatementAction { + public PublishObjectPropertyStatement(OntModel ontModel, String subjectUri, + Property keywordPred, String objectUri) { + super(ontModel, subjectUri, keywordPred, objectUri); + } + + public PublishObjectPropertyStatement(OntModel ontModel, + ObjectPropertyStatement ops) { + super(ontModel, ops); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/BaseResourceBean.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/BaseResourceBean.java index 58d51f031..02771a834 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/BaseResourceBean.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/BaseResourceBean.java @@ -25,63 +25,80 @@ public class BaseResourceBean implements ResourceBean { protected String localNameWithPrefix = null; protected String pickListName = null; - // these will be phased in and used in the filters Brian C. has been setting up, - // with hiddenFromDisplay to control the level at which any class, individual, object property, or data property is displayed - // and prohibitedFromEditing to control when a control for editing is made available protected RoleLevel hiddenFromDisplayBelowRoleLevel = null; - //protected RoleLevel prohibitedFromCreateBelowRoleLevel = null; protected RoleLevel prohibitedFromUpdateBelowRoleLevel = null; - //protected RoleLevel prohibitedFromDeleteBelowRoleLevel = null; + protected RoleLevel hiddenFromPublishBelowRoleLevel = null; - public enum RoleLevel { PUBLIC("http://vitro.mannlib.cornell.edu/ns/vitro/role#public","public","public"), - SELF("http://vitro.mannlib.cornell.edu/ns/vitro/role#selfEditor","self-authenticated","self"), - EDITOR("http://vitro.mannlib.cornell.edu/ns/vitro/role#editor","editor, curator, site administrator","editor"), - CURATOR("http://vitro.mannlib.cornell.edu/ns/vitro/role#curator","curator, site administrator","curator"), - DB_ADMIN("http://vitro.mannlib.cornell.edu/ns/vitro/role#dbAdmin","site administrator","siteAdmin"), - NOBODY("http://vitro.mannlib.cornell.edu/ns/vitro/role#nobody","root user","root"); - - private final String uri; - private final String label; - private final String shorthand; - - RoleLevel(String uriStr,String labelStr, String shortStr) { - this.uri = uriStr; - this.label = labelStr; - this.shorthand = shortStr; - } - - public String getURI() { - return uri; - } - - public String getLabel() { - return label; - } + public enum RoleLevel { + PUBLIC("http://vitro.mannlib.cornell.edu/ns/vitro/role#public", + "all users, including public", "all users who can log in", + "public"), + + SELF("http://vitro.mannlib.cornell.edu/ns/vitro/role#selfEditor", + "self-editor and above", "self-editor and above", "self"), + + EDITOR("http://vitro.mannlib.cornell.edu/ns/vitro/role#editor", + "editor and above", "editor and above", "editor"), + + CURATOR("http://vitro.mannlib.cornell.edu/ns/vitro/role#curator", + "curator and above", "curator and above", "curator"), + + DB_ADMIN("http://vitro.mannlib.cornell.edu/ns/vitro/role#dbAdmin", + "site admin and root user", "site admin and root user", + "siteAdmin"), + + NOBODY("http://vitro.mannlib.cornell.edu/ns/vitro/role#nobody", + "root user", "root user", "root"); + + private final String uri; + private final String displayLabel; + private final String updateLabel; + private final String shorthand; + + private RoleLevel(String uri, String displayLabel, String updateLabel, + String shorthand) { + this.uri = uri; + this.displayLabel = displayLabel; + this.updateLabel = updateLabel; + this.shorthand = shorthand; + } + + public String getURI() { + return uri; + } + + public String getDisplayLabel() { + return displayLabel; + } + + public String getUpdateLabel() { + return updateLabel; + } + + public String getShorthand() { + return shorthand; + } + + public static RoleLevel getRoleByUri(String uri2) { + if (uri2 == null) + return RoleLevel.values()[0]; + + for (RoleLevel role : RoleLevel.values()) { + if (role.uri.equals(uri2)) + return role; + } + return RoleLevel.values()[0]; + } - public String getShorthand() { - return shorthand; - } - - public static RoleLevel getRoleByUri(String uri2) { - if( uri2 == null ) - return RoleLevel.values()[0]; - - for( RoleLevel role : RoleLevel.values() ){ - if( role.uri.equals( uri2 ) ) - return role; - } - return RoleLevel.values()[0]; - } - public static RoleLevel getRoleFromLoginStatus(HttpServletRequest req) { UserAccount u = LoginStatusBean.getCurrentUser(req); if (u == null) { return PUBLIC; } - + Set roles = u.getPermissionSetUris(); if (roles.contains(PermissionSets.URI_DBA)) { - return DB_ADMIN; + return DB_ADMIN; } else if (roles.contains(PermissionSets.URI_CURATOR)) { return CURATOR; } else if (roles.contains(PermissionSets.URI_EDITOR)) { @@ -93,16 +110,19 @@ public class BaseResourceBean implements ResourceBean { return SELF; } } - } + } - public boolean isAnonymous() { + @Override + public boolean isAnonymous() { return (this.URI==null || VitroVocabulary.PSEUDO_BNODE_NS.equals(this.getNamespace())); } - public String getURI() { + @Override + public String getURI() { return URI; } - public void setURI(String URI) { + @Override + public void setURI(String URI) { if( this.localName != null || this.namespace != null) buildLocalAndNS(URI); else @@ -122,29 +142,34 @@ public class BaseResourceBean implements ResourceBean { } } - public String getNamespace() { + @Override + public String getNamespace() { if( namespace == null && this.URI != null) buildLocalAndNS(this.URI); return namespace; } - public void setNamespace(String namespace) { + @Override + public void setNamespace(String namespace) { this.namespace = namespace; if (namespace != null && localName != null ) { this.URI = namespace + localName; } } - public String getLabel() { + @Override + public String getLabel() { return getLocalName(); } - public String getLocalName() { + @Override + public String getLocalName() { if( localName == null && this.URI != null) buildLocalAndNS(this.URI); return localName; } - public void setLocalName(String localName) { + @Override + public void setLocalName(String localName) { this.localName = localName; if (namespace != null && localName != null) { this.URI = namespace + localName; @@ -160,7 +185,8 @@ public class BaseResourceBean implements ResourceBean { this.localNameWithPrefix = prefixedLocalName; } - public String getPickListName() { + @Override + public String getPickListName() { return pickListName==null ? getLocalName()==null ? (URI==null ? "(no name)" : URI ): getLocalName() : pickListName; } @@ -168,57 +194,51 @@ public class BaseResourceBean implements ResourceBean { this.pickListName = pickListName; } - public RoleLevel getHiddenFromDisplayBelowRoleLevel() { + @Override + public RoleLevel getHiddenFromDisplayBelowRoleLevel() { return hiddenFromDisplayBelowRoleLevel; } - public void setHiddenFromDisplayBelowRoleLevel(RoleLevel eR) { + @Override + public void setHiddenFromDisplayBelowRoleLevel(RoleLevel eR) { hiddenFromDisplayBelowRoleLevel = eR; } - public void setHiddenFromDisplayBelowRoleLevelUsingRoleUri(String roleUri) { + @Override + public void setHiddenFromDisplayBelowRoleLevelUsingRoleUri(String roleUri) { hiddenFromDisplayBelowRoleLevel = BaseResourceBean.RoleLevel.getRoleByUri(roleUri); } - /* - public RoleLevel getProhibitedFromCreateBelowRoleLevel() { - return prohibitedFromCreateBelowRoleLevel; - } - - public void setProhibitedFromCreateBelowRoleLevel(RoleLevel eR) { - prohibitedFromCreateBelowRoleLevel = eR; - } - - public void setProhibitedFromCreateBelowRoleLevelUsingRoleUri(String roleUri) { - prohibitedFromCreateBelowRoleLevel = BaseResourceBean.RoleLevel.getRoleByUri(roleUri); - } - */ - - public RoleLevel getProhibitedFromUpdateBelowRoleLevel() { + @Override + public RoleLevel getProhibitedFromUpdateBelowRoleLevel() { return prohibitedFromUpdateBelowRoleLevel; } - public void setProhibitedFromUpdateBelowRoleLevel(RoleLevel eR) { + @Override + public void setProhibitedFromUpdateBelowRoleLevel(RoleLevel eR) { prohibitedFromUpdateBelowRoleLevel = eR; } - public void setProhibitedFromUpdateBelowRoleLevelUsingRoleUri(String roleUri) { + @Override + public void setProhibitedFromUpdateBelowRoleLevelUsingRoleUri(String roleUri) { prohibitedFromUpdateBelowRoleLevel = BaseResourceBean.RoleLevel.getRoleByUri(roleUri); } - /* - public RoleLevel getProhibitedFromDeleteBelowRoleLevel() { - return prohibitedFromDeleteBelowRoleLevel; - } - - public void setProhibitedFromDeleteBelowRoleLevel(RoleLevel eR) { - prohibitedFromDeleteBelowRoleLevel = eR; - } - - public void setProhibitedFromDeleteBelowRoleLevelUsingRoleUri(String roleUri) { - prohibitedFromDeleteBelowRoleLevel = BaseResourceBean.RoleLevel.getRoleByUri(roleUri); - } - */ + @Override + public RoleLevel getHiddenFromPublishBelowRoleLevel() { + return hiddenFromPublishBelowRoleLevel; + } + + @Override + public void setHiddenFromPublishBelowRoleLevel(RoleLevel eR) { + hiddenFromPublishBelowRoleLevel = eR; + } + + @Override + public void setHiddenFromPublishBelowRoleLevelUsingRoleUri(String roleUri) { + hiddenFromPublishBelowRoleLevel = BaseResourceBean.RoleLevel.getRoleByUri(roleUri); + } + @Override public boolean equals(Object obj) { if(obj == null ) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ObjectProperty.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ObjectProperty.java index 7d1d908fb..26a84c601 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ObjectProperty.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ObjectProperty.java @@ -633,6 +633,7 @@ public class ObjectProperty extends Property implements Comparable 0) { @@ -116,12 +118,16 @@ public class EntityEditController extends BaseEditController { } results.add(classStr); - results.add(ent.getHiddenFromDisplayBelowRoleLevel() == null ? "unspecified" : ent.getHiddenFromDisplayBelowRoleLevel().getLabel()); - results.add(ent.getProhibitedFromUpdateBelowRoleLevel() == null ? "unspecified" : ent.getProhibitedFromUpdateBelowRoleLevel().getLabel()); + results.add(ent.getHiddenFromDisplayBelowRoleLevel() == null ? "unspecified" + : ent.getHiddenFromDisplayBelowRoleLevel().getDisplayLabel()); + results.add(ent.getProhibitedFromUpdateBelowRoleLevel() == null ? "unspecified" + : ent.getProhibitedFromUpdateBelowRoleLevel().getUpdateLabel()); String rModTime = (ent.getModTime()==null) ? "" : publicDateFormat.format(ent.getModTime()); results.add(rModTime); results.add( (ent.getURI() == null) ? "[anonymous individual]" : ent.getURI() ); + results.add(ent.getHiddenFromPublishBelowRoleLevel() == null ? "unspecified" + : ent.getHiddenFromPublishBelowRoleLevel().getDisplayLabel()); request.setAttribute("results",results); request.setAttribute("columncount", colCount); request.setAttribute("suppressquery","true"); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/EntityRetryController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/EntityRetryController.java index 3aade392a..b257515f4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/EntityRetryController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/EntityRetryController.java @@ -170,6 +170,7 @@ public class EntityRetryController extends BaseEditController { hash.put("HiddenFromDisplayBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getDisplayOptionsList(individualForEditing)); hash.put("ProhibitedFromUpdateBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getUpdateOptionsList(individualForEditing)); + hash.put("HiddenFromPublishBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getPublishOptionsList(individualForEditing)); FormObject foo = new FormObject(); foo.setOptionLists(hash); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyEditController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyEditController.java index b6d5dcd46..f57dee2c8 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyEditController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyEditController.java @@ -6,7 +6,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import javax.servlet.RequestDispatcher; @@ -21,17 +20,14 @@ import edu.cornell.mannlib.vedit.beans.FormObject; import edu.cornell.mannlib.vedit.controller.BaseEditController; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; -import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; import edu.cornell.mannlib.vitro.webapp.beans.Ontology; import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.controller.Controllers; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess; import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao; import edu.cornell.mannlib.vitro.webapp.dao.PropertyGroupDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; -import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao; public class PropertyEditController extends BaseEditController { @@ -43,7 +39,7 @@ public class PropertyEditController extends BaseEditController { return; } - final int NUM_COLS=24; + final int NUM_COLS=25; VitroRequest vreq = new VitroRequest(request); @@ -81,6 +77,7 @@ public class PropertyEditController extends BaseEditController { results.add("offer create new"); // column 22 results.add("sort direction"); // column 23 results.add("URI"); // column 24 + results.add("publish level"); // column 25 results.add(p.getPickListName()); // column 1 @@ -169,8 +166,10 @@ public class PropertyEditController extends BaseEditController { String descriptionStr = (p.getDescription() == null) ? "" : p.getDescription(); results.add(descriptionStr); // column 15 - results.add(p.getHiddenFromDisplayBelowRoleLevel() == null ? "(unspecified)" : p.getHiddenFromDisplayBelowRoleLevel().getLabel()); // column 16 - results.add(p.getProhibitedFromUpdateBelowRoleLevel() == null ? "(unspecified)" : p.getProhibitedFromUpdateBelowRoleLevel().getLabel()); // column 17 + results.add(p.getHiddenFromDisplayBelowRoleLevel() == null ? "(unspecified)" + : p.getHiddenFromDisplayBelowRoleLevel().getDisplayLabel()); // column 16 + results.add(p.getProhibitedFromUpdateBelowRoleLevel() == null ? "(unspecified)" + : p.getProhibitedFromUpdateBelowRoleLevel().getUpdateLabel()); // column 17 results.add("property: "+p.getDomainDisplayTier() + ", inverse: "+p.getRangeDisplayTier()); // column 18 @@ -183,6 +182,8 @@ public class PropertyEditController extends BaseEditController { results.add(p.getDomainEntitySortDirection() == null ? "ascending" : p.getDomainEntitySortDirection()); // column 23 results.add(p.getURI()); // column 24 + results.add(p.getHiddenFromPublishBelowRoleLevel() == null ? "(unspecified)" + : p.getHiddenFromPublishBelowRoleLevel().getDisplayLabel()); // column 25 request.setAttribute("results",results); request.setAttribute("columncount",NUM_COLS); request.setAttribute("suppressquery","true"); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java index 580a8c861..1d2647c4f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/PropertyRetryController.java @@ -151,6 +151,7 @@ public class PropertyRetryController extends BaseEditController { optionMap.put("HiddenFromDisplayBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getDisplayOptionsList(propertyForEditing)); optionMap.put("ProhibitedFromUpdateBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getUpdateOptionsList(propertyForEditing)); + optionMap.put("HiddenFromPublishBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getPublishOptionsList(propertyForEditing)); List groupOptList = FormUtils.makeOptionListFromBeans(request.getUnfilteredWebappDaoFactory().getPropertyGroupDao().getPublicGroups(true),"URI","Name", ((propertyForEditing.getGroupURI()==null) ? "" : propertyForEditing.getGroupURI()), null, (propertyForEditing.getGroupURI()!=null)); HashMap hashMap = new HashMap(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java index 69960f546..61cb59362 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java @@ -33,7 +33,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.Ontology; public class VclassEditController extends BaseEditController { private static final Log log = LogFactory.getLog(VclassEditController.class.getName()); - private static final int NUM_COLS = 13; + private static final int NUM_COLS = 14; public void doPost (HttpServletRequest req, HttpServletResponse response) { if (!isAuthorizedToDisplayPage(req, response, SimplePermission.EDIT_ONTOLOGY.ACTIONS)) { @@ -70,6 +70,7 @@ public class VclassEditController extends BaseEditController { results.add("display rank"); // 11 results.add("custom entry form"); // 12 results.add("URI"); // 13 + results.add("publish level"); // 14 String ontologyName = null; if (vcl.getNamespace() != null) { @@ -107,8 +108,13 @@ public class VclassEditController extends BaseEditController { commSb = new StringBuffer("no comments yet"); } - String hiddenFromDisplay = (vcl.getHiddenFromDisplayBelowRoleLevel() == null ? "(unspecified)" : vcl.getHiddenFromDisplayBelowRoleLevel().getLabel()); - String ProhibitedFromUpdate = (vcl.getProhibitedFromUpdateBelowRoleLevel() == null ? "(unspecified)" : vcl.getProhibitedFromUpdateBelowRoleLevel().getLabel()); + String hiddenFromDisplay = (vcl.getHiddenFromDisplayBelowRoleLevel() == null ? "(unspecified)" + : vcl.getHiddenFromDisplayBelowRoleLevel().getDisplayLabel()); + String ProhibitedFromUpdate = (vcl + .getProhibitedFromUpdateBelowRoleLevel() == null ? "(unspecified)" + : vcl.getProhibitedFromUpdateBelowRoleLevel().getUpdateLabel()); + String hiddenFromPublish = (vcl.getHiddenFromPublishBelowRoleLevel() == null ? "(unspecified)" + : vcl.getHiddenFromPublishBelowRoleLevel().getDisplayLabel()); String customEntryForm = (vcl.getCustomEntryForm() == null ? "(unspecified)" : vcl.getCustomEntryForm()); @@ -130,6 +136,7 @@ public class VclassEditController extends BaseEditController { results.add(String.valueOf(vcl.getDisplayRank())); // 11 results.add(customEntryForm); // 12 results.add(uri); // 13 + results.add(hiddenFromPublish); // 14 request.setAttribute("results", results); request.setAttribute("columncount", NUM_COLS); request.setAttribute("suppressquery", "true"); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java index 3b811f200..46d970ec5 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassRetryController.java @@ -147,6 +147,7 @@ public class VclassRetryController extends BaseEditController { optionMap.put("HiddenFromDisplayBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getDisplayOptionsList(vclassForEditing)); optionMap.put("ProhibitedFromUpdateBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getUpdateOptionsList(vclassForEditing)); + optionMap.put("HiddenFromPublishBelowRoleLevelUsingRoleUri",RoleLevelOptionsSetup.getPublishOptionsList(vclassForEditing)); FormObject foo = new FormObject(); foo.setErrorMap(epo.getErrMsgMap()); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/utils/RoleLevelOptionsSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/utils/RoleLevelOptionsSetup.java index 89f8f0854..29aa7bd75 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/utils/RoleLevelOptionsSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/utils/RoleLevelOptionsSetup.java @@ -23,7 +23,7 @@ public class RoleLevelOptionsSetup { boolean someLevelSet=false; Option publicOption = null; for (BaseResourceBean.RoleLevel level : roles) { - Option option = new Option (level.getURI(),level.getLabel(),false); + Option option = new Option (level.getURI(),level.getDisplayLabel(),false); if (level==BaseResourceBean.RoleLevel.PUBLIC) { publicOption = option; } @@ -50,7 +50,7 @@ public class RoleLevelOptionsSetup { boolean someLevelSet=false; Option publicOption = null; for (BaseResourceBean.RoleLevel level : roles) { - Option option = new Option (level.getURI(),level.getLabel(),false); + Option option = new Option (level.getURI(),level.getUpdateLabel(),false); if (level==BaseResourceBean.RoleLevel.PUBLIC) { publicOption = option; } @@ -68,4 +68,33 @@ public class RoleLevelOptionsSetup { } return prohibitedFromUpdateList; } + + public static List