Before beginning the installation,
@@ -54,8 +59,8 @@
This is created when you checkout the Vitro source code
(see installation step III, below).
- This is where you will create your deploy.properties file
- (see installation step V, below),
+ This is where you will create your build.properties file
+ (see installation step IV, below),
and where you will make any modifications to the Vitro
theme or code. You can create this wherever you choose.
@@ -68,18 +73,20 @@
but you won’t need to look at it or change it. If you need to change
Vitro, make the changes in the distribution directory, and run the build
script again. Tell the build script where to find Tomcat by setting tomcat.home
- in the deploy.properties file (see installation step V,
+ in the build.properties file (see installation step IV,
below).
The Vitro home directory
- Vitro will use this area to store some of the data it uses. Uploaded
- image files are stored here, and the search index is stored here also.
+ This directory contains the runtime configuration properties for Vitro.
+ Vitro will also use this area to store some of the data it uses. Uploaded
+ image files are stored here, and the Solr home directory is stored here also.
You can create this wherever you choose. Tell Vitro where to find the
- home directory by setting vitro.home.directory
- in the
- deploy.properties file (see installation step V,
- below). You must create this directory before starting Vitro, and you
+ home directory by setting vitro.home in the
+ build.properties file (see installation step IV,
+ below). You must create this directory before starting Vitro,
+ you must create the runtime.properties file in this directory
+ (see Step V, below), and you
must ensure that Tomcat has permission to read and write to this
directory when it runs.
In the webapp/config directory of the Vitro distribution,
- copy the file example.deploy.properties
- to a file named simply deploy.properties. This file sets
- several properties used in compilation and deployment.
+ copy the file example.build.properties
+ to a file named simply build.properties.
+ Edit the file to suit your installation, as described in the following table.
+
+
+ These properties are used in compilation and deployment.
+ They will be incorporated into Vitro when it is compiled in
+ Step VI. If you want to change these properties at
+ a later date, you will need to stop Tomcat, repeat Step VI,
+ and restart Tomcat.
+
+
+ Windows:
+ For those installing on Windows operating
+ system, include the windows drive and use the forward slash "/" and not
+ the back slash "\" in the directory locations, e.g. c:/tomcat.
+
+
+
+
+
+ Directory where tomcat is installed.
+
+
+
+
+ tomcat.home
+
+
+ /usr/local/tomcat
+
+
+
+
+ Name of your Vitro application.
+
+
+
+
+ webapp.name
+
+
+ vitro
+
+
+
+
+ Directory where the Vitro application will store
+ the data that it creates. This includes uploaded files (usually images)
+ and the Solr search index. Be sure this directory exists and is
+ writable by the user who the Tomcat service is running as.
+
+
+
+
+ vitro.home
+
+
+ /usr/local/vitro/home
+
+
+
+
+
+
+
V. Specify runtime properties
+
+ In Step IV, you defined the location of the Vitro home directory,
+ by specifying vitro.home in the build.properties file.
+ Create that directory now.
+
+
+ In the webapp/config subdirectory of the Vitro distribution, you will find a file called
+ example.runtime.properties. Copy this to the Vitro home directory you have created,
+ renaming the copy to runtime.properties.
+ Edit the file to suit your installation, as described in the following table.
+
+
+ These properties are loaded when Vitro starts up. If you want to change these
+ properties at a later date, you will need to restart Tomcat for them to take
+ effect. You will not need to repeat Step VI.
URL of Solr context used in local Vitro search.
@@ -372,53 +435,6 @@
http://localhost:8080/vitrosolr
-
-
- Restricts access to the Solr search platform.
- The value is a regular expression. When a request is
- made to Solr, the IP address of the requestor must the expression,
- or the request will be rejected.
-
- Examples:
-
- Directory where the Vitro application will store
- the data that it creates. This includes uploaded files (usually images)
- and the Solr search index. Be sure this directory exists and is
- writable by the user who the Tomcat service is running as.
-
-
-
-
- vitro.home.directory
-
-
- /usr/local/vitro/data
-
-
Specify an SMTP host that the application will
@@ -635,13 +651,101 @@
- An absolute file path, pointing to the root directory of the Harvester utility.
- You must include the final slash.
+ Force VIVO to use a specific language or Locale instead of those
+ specified by the browser.
+ This affects RDF data retrieved from the model, if RDFService.languageFilter is true.
+ This also affects the text of pages that have been modified to support multiple languages.
+
+
+ languages.forceLocale
+
+
+ en_US
+
+
+
+
+
+ A list of supported languages or Locales that the user may choose to
+ use instead of the one specified by the browser. Selection images must
+ be available in the i18n/images directory of the theme.
+ This affects RDF data retrieved from the model, if RDFService.languageFilter is true.
+ This also affects the text of pages that have been modified to support multiple languages.
+
+
+
+
+ languages.selectableLocales
+
+
+ en, es, fr_FR
+
+
+
+
+
+ For developers only.
+ Defeat the Freemarker template cache, so each template
+ is read from disk on each request. This permits developers to immediately
+ see the effect of changes to the template. The default is false, which
+ means that a cached copy of each template will be used for 60 seconds
+ before the disk is checked for a new version.
+ Setting this option to "true" slows down Vitro performance.
+
+
+
+
+ developer.defeatFreemarkerCache
+
+
+ false
+
+
+
+
+
+ For developers only.
+ Defeat the cache of language-specific text strings,
+ so the language file is read from disk on each request.
+ This permits developers to immediately
+ see the effect of changes to the text strings.
+ The default is false, which means that the language file is
+ read when VIVO starts up, or when a new theme is selected.
+ Setting this option to "true" slows down Vitro performance.
+
+
+
+
+ developer.defeatI18nCache = true
+
+
+ false
+
+
+
+
+
+ For developers only.
+ Add starting and ending delimiters to each Freemarker template, so you can see
+ which template were invoked by viewing the generated HTML.
+ The default is false.
+ Setting this option to "true" slows down Vitro performance.
+
+
+
+
+ developer.insertFreemarkerDelimiters = true
+
+
+ false
+
+
+
-
V. Compile and deploy
+
VI. Compile and deploy
At the command line, change to the webapp directory inside the Vitro distribution
directory. Then type:
@@ -650,7 +754,7 @@
to build Vitro and deploy to Tomcat's webapps directory.
-
VI. Configure Tomcat
+
VII. Configure Tomcat
Set JVM parameters
Vitro may require more memory than that allocated to Tomcat by
@@ -686,7 +790,7 @@
accepting percent-encoded UTF-8.
- Edit Tomcat's conf/server.xml and add the following attribute to each of the
+ Edit Tomcat's conf/server.xml and add the following attribute to each of the
Connector elements: URIEncoding="UTF-8".
@@ -699,6 +803,9 @@
</Server>
+ Note: some versions of Tomcat already include this attribute as the default.
+
+
Take care when creating Context elements
Each of the webapps in the Vitro distribution (Vitro and Solr) includes a "context fragment" file,
@@ -710,22 +817,35 @@
deployment parameters from the overridden context fragment.
- See Section X below,
+ See Section XI below,
for an example of overriding the Vitro context fragment.
-
VII. Start Tomcat
+
VIII. Start Tomcat
Most Tomcat installations can be started by running startup.sh
or startup.bat
in Tomcat's bin directory. Point your
- browser to "http://localhost:8080/vitro/" to test the application. If
- Tomcat does not start up, or the Vitro application is not visible, check
- the files in Tomcat's logs directory. Error messages are commonly found
+ browser to "http://localhost:8080/vitro/" to test the application.
+
+
+ On start up Vitro will run some diagnostic tests. If a
+ problem is detected, the normal Vitro pages will redirect
+ to a startup status page describing the problem. You
+ can stop tomcat, attempt to fix the problem and
+ proceeded from Step VI. The
+ startup status page may offer a continue link which
+ will allow you to use VIVO in spite of the problems.
+
+
+ If Tomcat does not start up, or the Vitro application is not visible, check
+ the files in Tomcat's logs directory. Error messages are commonly found
in catalina.out
or localhost.log
-
VIII. Log in and add RDF data
+
+
+
IX. Log in and add RDF data
If the startup was successful, you will see a welcome message
informing you that you have successfully installed Vitro. Click the "Log
@@ -759,7 +879,7 @@
the page will show a simple index of the knowledge base.
-
IX. Set the Contact Email Address (if using
+
X. Set the Contact Email Address (if using
"Contact Us" form)
If you have configured your application to use the "Contact Us"
@@ -781,7 +901,7 @@
provide an email address in this step, your users will receive a java
error in the interface.
-
X. Set up Apache Tomcat Connector
+
XI. Set up Apache Tomcat Connector
It is recommended that a Tomcat Connector such as mod_jk be used to
ensure that the site address does not include the port number (e.g.
@@ -832,16 +952,11 @@
cookies="true" >
<Manager pathname="" />
-
- <Environment type="java.lang.String" override="false"
- name="path.configuration"
- value="deploy.properties"
- />
</Context>
...
-
XI. Using an External Authentication System
+
XII. Using an External Authentication System
with Vitro
@@ -878,7 +993,7 @@
Configuring Vitro
To enable external authentication, Vitro requires three values in
- the deploy.properties
+ the runtime.properties
file.
@@ -893,7 +1008,7 @@
You need to tell Vitro the name of that HTTP header. Insert a
- line like this in the deploy.properties file:
+ line like this in the runtime.properties file:
externalAuth.netIdHeaderName = [the header name]
@@ -907,7 +1022,7 @@
the Vitro login form. You need to tell Vitro what text should appear in
that button.
- Put a line like this in the deploy.properties file:
+ Put a line like this in the runtime.properties file:
externalAuth.buttonText = [the text for your login button] For example:
externalAuth.buttonText = Log in using BearCat Shibboleth
@@ -924,7 +1039,7 @@
data model for a person with a property that matches the User’s network
ID (the value of the property must be either a String literal or an
untyped literal). You need to tell Vitro what property should be used
- for matching. Insert a line like this in the deploy.properties file:
+ for matching. Insert a line like this in the runtime.properties file:
selfEditing.idMatchingProperty = [the URI of the property]
If you have completed the previous steps, you have good indications
that the installation was successful.
- Step VII showed that Tomcat recognized the webapp, and that the
+ Step VIII showed that Tomcat recognized the webapp, and that the
webapp was able to present the initial page.
- Step VIII verified that you can log in to the administrator
+ Step IX verified that you can log in to the administrator
account.
- Step VII also shows that the Vitro self-tests ran successfully.
+ Step VIII also shows that the Vitro self-tests ran successfully.
When Tomcat starts the Vitro webapp, it runs several tests.
If any of these tests produce warnings or error message,
you would see them instead of the Vitro home page.
Among other things, the self-tests check
-
-
-
The Vitro home directory exists and Vitro can write to it.
-
Vitro can connect to the database.
-
Vitro can connect to the Solr search application.
-
-
+
+
The Vitro home directory exists and Vitro can write to it.
+
Vitro can connect to the database.
+
Vitro can connect to the Solr search application.
+
If you saw the Vitro home page in Step VII, you know that your installation passed all of these tests.
-
XIII. Review the Vitro Terms of Use
+
XIV. Review the Vitro Terms of Use
Vitro comes with a "Terms of Use" statement linked from the footer.
The "Site Name" you assign in the "Site Information" form under the Site Admin
@@ -992,7 +1105,7 @@
diff --git a/opensocial/build_orng.xml b/opensocial/build_orng.xml
index de883712c..02ceabc85 100644
--- a/opensocial/build_orng.xml
+++ b/opensocial/build_orng.xml
@@ -30,40 +30,53 @@ deploy - Configure the application and deploy directly into the Tomcat webapps
target: properties
- - - - - - - - - - - - - - - - - -->
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
@@ -83,7 +96,7 @@ deploy - Configure the application and deploy directly into the Tomcat webapps
-
+
@@ -145,5 +158,4 @@ deploy - Configure the application and deploy directly into the Tomcat webapps
-
\ No newline at end of file
diff --git a/solr/apache-solr-3.1.0.war b/solr/apache-solr-3.1.0.war
deleted file mode 100644
index 7c27bb483..000000000
Binary files a/solr/apache-solr-3.1.0.war and /dev/null differ
diff --git a/solr/apache-solr-3.6.2.war b/solr/apache-solr-3.6.2.war
new file mode 100644
index 000000000..8144c4119
Binary files /dev/null and b/solr/apache-solr-3.6.2.war differ
diff --git a/solr/homeDirectoryTemplate/conf/schema.xml b/solr/homeDirectoryTemplate/conf/schema.xml
index 0292cd5f8..b784f33c9 100644
--- a/solr/homeDirectoryTemplate/conf/schema.xml
+++ b/solr/homeDirectoryTemplate/conf/schema.xml
@@ -253,7 +253,7 @@
-
+
diff --git a/solr/template.context.xml b/solr/template.context.xml
index 6d1f87578..8244079c8 100644
--- a/solr/template.context.xml
+++ b/solr/template.context.xml
@@ -1,11 +1,4 @@
-
-
-
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-all - Runs "clean", then "deploy".
-clean - Delete all artifacts so the next build will be from scratch.
-compile - Compile the Java source files.
-orng - Configure and deploy the ORNG Shindig application.
-test - Compile and run the JUnit tests.
-war - Create a WAR file to be deployed in a servlet container.
-deploy - Deploy the application directly into the Tomcat webapps directory.
-
-
-
-
-
-
-
-
-
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -227,112 +237,160 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
-
-
-
-
+
- ${revisionInfo.timestamp}
+ ${revisionInfo.timestamp}
-
-
-
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+all - Runs "clean", then "deploy".
+clean - Delete all artifacts so the next build will be from scratch.
+compile - Compile the Java source files.
+orng - Configure and deploy the ORNG Shindig application.
+test - Compile and run the JUnit tests.
+distribute - Create WAR files to be deployed in a servlet container.
+deploy - Deploy the application directly into the Tomcat webapps directory.
+
+
+
+
+
+
-
+
@@ -393,13 +451,16 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
+
-
+
+
\ No newline at end of file
diff --git a/webapp/config/example.build.properties b/webapp/config/example.build.properties
new file mode 100644
index 000000000..cd1a293ff
--- /dev/null
+++ b/webapp/config/example.build.properties
@@ -0,0 +1,29 @@
+# -----------------------------------------------------------------------------
+#
+# Vitro build properties
+#
+# This file is provided as example.build.properties.
+#
+# Save a copy of this file as build.properties, and edit the properties as
+# needed for your installation.
+#
+# -----------------------------------------------------------------------------
+
+#
+# The base install directory for your Tomcat server. The Vitro application
+# will be deployed in the /webapps directory below this base.
+#
+tomcat.home = /usr/local/tomcat
+
+#
+# The name of the Vitro application. This will be used as the name of the
+# subdirectory within your Tomcat server's /webapps directory. It also appears
+# in the URL for the application. For example, http://my.vitro.server/vitro
+#
+webapp.name = vitro
+
+#
+# The location where the Vitro application will store the data that it creates.
+# This includes uploaded files (usually images) and the search index.
+#
+vitro.home = /usr/local/vitro/home
diff --git a/webapp/config/example.deploy.properties b/webapp/config/example.runtime.properties
similarity index 63%
rename from webapp/config/example.deploy.properties
rename to webapp/config/example.runtime.properties
index 9b66f1385..3802063d2 100644
--- a/webapp/config/example.deploy.properties
+++ b/webapp/config/example.runtime.properties
@@ -1,11 +1,11 @@
# -----------------------------------------------------------------------------
#
-# Vitro deployment properties
+# Vitro runtime properties
#
-# This file is provided as example.deploy.properties.
+# This file is provided as example.runtime.properties.
#
-# Save a copy of this file as deploy.properties, and edit the properties as
-# needed for your deployment.
+# Save a copy of this file as runtime.properties in your Vitro home directory,
+# and edit the properties as needed for your installation.
#
# -----------------------------------------------------------------------------
@@ -22,19 +22,6 @@
#
Vitro.defaultNamespace = http://vivo.mydomain.edu/individual/
-#
-# The base install directory for your Tomcat server. The Vitro application
-# will be deployed in the /webapps directory below this base.
-#
-tomcat.home = /usr/local/tomcat
-
-#
-# The name of the Vitro application. This will be used as the name of the
-# subdirectory within your Tomcat server's /webapps directory. It also appears
-# in the URL for the application. For example, http://my.vitro.server/vitro
-#
-webapp.name = vitro
-
#
# URL of Solr context used in local Vitro search. This will usually consist of:
# scheme + server_name + port + vitro_webapp_name + "solr"
@@ -45,28 +32,6 @@ webapp.name = vitro
# vitro.local.solr.url = http://localhost:8080/vitrosolr
vitro.local.solr.url = http://localhost:8080/vitrosolr
-#
-# Restricts access to the Solr search platform. The value is a regular expression.
-# When a request is made to Solr, the IP address of the requestor must match the
-# regular expression, or the request will be rejected.
-#
-# NOTE: don't leave a space on the end of the line, unless you want it to be
-# part of the expression.
-# Examples:
-# vitro.local.solr.ipaddress.mask = 127\.0\.0\.1
-# vitro.local.solr.ipaddress.mask = 127\.0\.0\.1|0:0:0:0:0:0:0:1
-# vitro.local.solr.ipaddress.mask = 169\.254\..*
-# If this line is removed, your Solr server will respond to requests from any
-# location. This may be useful for experimenting but would likely be considered
-# a security problem in a production environment.
-vitro.local.solr.ipaddress.mask = 127\.0\.0\.1|[0:]+:1
-
-#
-# The location where the Vitro application will store the data that it creates.
-# This includes uploaded files (usually images) and the search index.
-#
-vitro.home.directory = /usr/local/vitro/data
-
#
# Email parameters which VIVO can use to send mail. If these are left empty,
# the "Contact Us" form will be disabled and users will not be notified of
@@ -154,3 +119,55 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing
# header supplied by the browser. Default is true if not set.
#
RDFService.languageFilter = true
+
+#
+# Force VIVO to use a specific language or Locale instead of those
+# specified by the browser. This affects RDF data retrieved from the model,
+# if RDFService.languageFilter is true. This also affects the text of pages
+# that have been modified to support multiple languages.
+#
+# languages.forceLocale = en_US
+
+#
+# A list of supported languages or Locales that the user may choose to
+# use instead of the one specified by the browser. Selection images must
+# be available in the i18n/images directory of the theme. This affects
+# RDF data retrieved from the model, if RDFService.languageFilter is true.
+# This also affects the text of pages that have been modified to support
+# multiple languages.
+#
+# This should not be used with languages.forceLocale, which will override it.
+#
+# languages.selectableLocales = en, es, fr
+
+#
+# For developers only: Setting this option to "true" slows down Vitro performance.
+#
+# Defeat the Freemarker template cache, so each template is read from disk
+# on each request. This permits developers to immediately see the effect of
+# changes to the template. The default is false, which means
+# that a cached copy of each template will be used for 60 seconds before
+# the disk is checked for a new version.
+#
+# developer.defeatFreemarkerCache = true
+
+#
+# For developers only: Setting this option to "true" slows down Vitro performance.
+#
+# Defeat the cache of language-specific text strings, so the language file
+# is read from disk on each request. This permits developers to immediately
+# see the effect of changes to the text strings. The default is
+# false, which means that the language file is read when
+# VIVO starts up, or when a new theme is selected.
+#
+# developer.defeatI18nCache = true
+
+#
+# For developers only: Setting this option to "true" slows down Vitro performance.
+#
+# Add starting and ending delimiters to each Freemarker template, so you can see
+# which template were invoked by viewing the generated HTML. The default is
+# false.
+#
+# developer.insertFreemarkerDelimiters = true
+
diff --git a/webapp/config/licenser/known_exceptions.txt b/webapp/config/licenser/known_exceptions.txt
index 2551ba6f7..6efdabf02 100644
--- a/webapp/config/licenser/known_exceptions.txt
+++ b/webapp/config/licenser/known_exceptions.txt
@@ -165,6 +165,9 @@ webapp/web/js/raphael/*
# See /doc/3rd-party-licenses.txt for LICENSE file
webapp/web/js/sparql/prototype.js
+# See /doc/3rd-party-licenses.txt for LICENSE file
+webapp/web/js/amplify/amplify.store.min.js
+
# Apache Solr search platform. See /doc/3rd-party-licenses.txt for LICENSE file
solr/**/*
solr/*
diff --git a/webapp/context.xml b/webapp/context.xml
index 364c41b35..b0cd304f7 100644
--- a/webapp/context.xml
+++ b/webapp/context.xml
@@ -1,20 +1,5 @@
-
-
-
-
-
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java
index 03234a2a1..9c635a6cf 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java
@@ -120,7 +120,7 @@ public class RootUserPolicy implements PolicyIface {
PROPERTY_ROOT_USER_EMAIL);
if (email == null) {
throw new IllegalStateException(
- "deploy.properties must contain a value for '"
+ "runtime.properties must contain a value for '"
+ PROPERTY_ROOT_USER_EMAIL + "'");
} else {
return email;
@@ -177,7 +177,7 @@ public class RootUserPolicy implements PolicyIface {
private void complainAboutMultipleRootUsers() {
for (String other : otherRootUsers) {
- ss.warning(this, "deploy.properties specifies '"
+ ss.warning(this, "runtime.properties specifies '"
+ configuredRootUser + "' as the value for '"
+ PROPERTY_ROOT_USER_EMAIL
+ "', but the system also contains this root user: "
@@ -189,7 +189,7 @@ public class RootUserPolicy implements PolicyIface {
private void complainAboutWrongRootUsers() {
for (String other : otherRootUsers) {
- ss.warning(this, "deploy.properties specifies '"
+ ss.warning(this, "runtime.properties specifies '"
+ configuredRootUser + "' as the value for '"
+ PROPERTY_ROOT_USER_EMAIL
+ "', but the system contains this root user instead: "
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java
index bcf83a979..99dec7616 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/ApplicationBean.java
@@ -49,15 +49,6 @@ public class ApplicationBean {
private String copyrightAnchor;
private String themeDir;
- public static ApplicationBean getAppBean(ServletContext sc){
- if( sc != null ){
- Object obj = sc.getAttribute("applicationBean");
- if( obj != null )
- return (ApplicationBean)obj;
- }
- return new ApplicationBean();
- }
-
public String toString() {
String output = "Application Bean Contents:\n";
output += " initialized from DB: [" + initialized + "]\n";
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java
index 0109c9286..74112fc15 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java
@@ -20,8 +20,8 @@ import org.apache.commons.logging.LogFactory;
* are attached to the servlet context.
*
* The customary behavior is for ConfigurationPropertiesSetup to create a
- * ConfigurationPropertiesImpl, which will parse the deploy.properties file for
- * these properties.
+ * ConfigurationPropertiesImpl, which will obtain the properties from the
+ * build.properties file and the runtime.properties file.
*/
public abstract class ConfigurationProperties {
private static final Log log = LogFactory
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java
index f0440b5de..1361d350a 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java
@@ -9,13 +9,16 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The basic implementation of ConfigurationProperties. It loads the
- * configuration properties from a properties file and stores them in a map.
+ * configuration properties from a properties file and stores them in a map. It
+ * also permits the caller to supply a map of "preemptive" properties that will
+ * be included and will override any matching properties from the file.
*
* Leading and trailing white space are trimmed from the property values.
*
@@ -27,23 +30,25 @@ public class ConfigurationPropertiesImpl extends ConfigurationProperties {
private final Map propertyMap;
- public ConfigurationPropertiesImpl(InputStream stream) {
+ public ConfigurationPropertiesImpl(InputStream stream,
+ Map preemptiveProperties) throws IOException {
Properties props = loadFromPropertiesFile(stream);
Map map = copyPropertiesToMap(props);
- trimWhiteSpaceFromValues(map);
- this.propertyMap = Collections.unmodifiableMap(map);
+ if (preemptiveProperties != null) {
+ map.putAll(preemptiveProperties);
+ }
+
+ trimWhiteSpaceFromValues(map);
+
+ this.propertyMap = Collections.unmodifiableMap(map);
log.debug("Configuration properties are: " + map);
}
- private Properties loadFromPropertiesFile(InputStream stream) {
+ private Properties loadFromPropertiesFile(InputStream stream)
+ throws IOException {
Properties props = new Properties();
- try {
- props.load(stream);
- } catch (IOException e) {
- throw new IllegalStateException(
- "Failed to parse the configuration properties file.", e);
- }
+ props.load(stream);
return props;
}
@@ -84,7 +89,8 @@ public class ConfigurationPropertiesImpl extends ConfigurationProperties {
@Override
public String toString() {
- return "ConfigurationPropertiesImpl[propertyMap=" + propertyMap + "]";
+ return "ConfigurationPropertiesImpl[propertyMap="
+ + new TreeMap(propertyMap) + "]";
}
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java
index 82318a4de..b5b5da3fa 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java
@@ -4,11 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.config;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
-import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
@@ -21,61 +23,45 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
/**
- * Reads the configuration properties from a file and stores them in the servlet
+ * Locates the runtime configuration properties and stores them in the servlet
* context.
*
* This must be invoked before any listener that requires configuration
* properties.
*
- * The path to the file can be specified by an Environment name in the Context,
- * like this:
+ * The properties file must be called 'runtime.properties' in the Vitro home
+ * directory. The path to the Vitro home directory can be specifed by an JNDI
+ * value, or by a System property, or by a property in
+ * WEB-INF/resources/build.properties, in that order. If the Vitro home
+ * directory is specified in more than one way, a warning is issued and the
+ * first value is used.
*
- *
+ * If the Vitro home directory cannot be located, or if it does not contain a
+ * file called 'runtime.properties', or if the file cannot be loaded, a fatal
+ * error is registered to abort the startup.
*
- *
- *
- *
- *
- *
- *
- * We look in this environment variable to find the path to the properties file.
- * If there is no such environment variable, the default path is used.
- *
- * Once the path has been determined, we will use it to look for a resource in
- * the classpath. So if the path is "deploy.properties", it might be found in
- * "tomcat/webapps/vivo/WEB-INF/classes/deploy.properties". Of course, it might
- * also be found in any other portion of the classpath as well.
- *
- * If we can't find the resource in the classpath, we will use it to look for an
- * external file. So, one might reasonably set this value to something like
- * "/usr/local/vitro/stuff/my.deploy.properties".
- *
- * If neither a resource nor an external file can be found, we throw an
- * exception and set AbortStartup.
+ * The ConfigurationProperties bean is created from the key/value pairs found in
+ * 'runtime.properties', and is stored in the servlet context. The value that
+ * was determined for 'vitro.home' is also included in the bean.
*/
public class ConfigurationPropertiesSetup implements ServletContextListener {
private static final Log log = LogFactory
.getLog(ConfigurationPropertiesSetup.class);
- /**
- * The JNDI naming context where Tomcat stores environment attributes.
- */
- static final String JNDI_BASE = "java:comp/env";
+ /** JNDI path that defines the Vitro home directory */
+ private static final String VHD_JNDI_PATH = "java:comp/env/vitro/home";
- /**
- * The name of the JNDI environment mapping for the path to the
- * configuration file (or resource).
- */
- static final String PATH_CONFIGURATION = "path.configuration";
+ /** System property that defines the Vitro home directory */
+ private static final String VHD_SYSTEM_PROPERTY = "vitro.home";
- /**
- * If we don't find the path to the config properties from a JNDI mapping,
- * use this. Not final, so we can jigger it for unit tests.
- */
- private static String DEFAULT_CONFIG_PATH = "deploy.properties";
+ /** build.properties property that defines the Vitro home directory */
+ private static final String VHD_BUILD_PROPERTY = "vitro.home";
+
+ /** Configuration property to store the Vitro home directory */
+ private static final String VHD_CONFIGURATION_PROPERTY = "vitro.home";
+
+ /** Name of the file that contains runtime properties. */
+ private static final String FILE_RUNTIME_PROPERTIES = "runtime.properties";
@Override
public void contextInitialized(ServletContextEvent sce) {
@@ -85,12 +71,18 @@ public class ConfigurationPropertiesSetup implements ServletContextListener {
try {
InputStream stream = null;
try {
- stream = locatePropertiesFile();
-
+ File vitroHomeDir = locateVitroHomeDirectory(ctx, ss);
+
+ File runtimePropertiesFile = locateRuntimePropertiesFile(
+ vitroHomeDir, ss);
+ stream = new FileInputStream(runtimePropertiesFile);
+
+ Map preempts = createPreemptiveProperties(
+ VHD_CONFIGURATION_PROPERTY, vitroHomeDir);
ConfigurationPropertiesImpl bean = new ConfigurationPropertiesImpl(
- stream);
+ stream, preempts);
+
ConfigurationProperties.setBean(ctx, bean);
-
ss.info(this, "Loaded " + bean.getPropertyMap().size()
+ " properties.");
} finally {
@@ -102,81 +94,171 @@ public class ConfigurationPropertiesSetup implements ServletContextListener {
}
}
}
+ } catch (IllegalStateException e) {
+ ss.fatal(this, e.getMessage(), e);
} catch (Exception e) {
- log.error(e, e);
ss.fatal(this, e.getMessage(), e);
}
}
- private InputStream locatePropertiesFile() {
- String path = determinePathToProperties();
- log.debug("Configuration properties path is '" + path + "'");
-
- if (resourceExists(path)) {
- log.debug("Found configuration properties as a resource.");
- return getResourceStream(path);
- }
-
- if (externalFileExists(path)) {
- log.debug("Found configuration properties as an external file.");
- return getExternalFileStream(path);
- }
-
- throw new IllegalStateException("Can't find the properties file at '"
- + path + "'");
- }
-
/**
- * If we can't find it with JNDI, use the default.
+ * Look in the JDNI environment, the system properties, and the
+ * build.properties file.
+ *
+ * If we don't find it, fail. If we find it more than once, warn and use the
+ * first one.
+ *
+ * Confirm that it is an existing, readable directory.
*/
- private String determinePathToProperties() {
+ private File locateVitroHomeDirectory(ServletContext ctx, StartupStatus ss) {
+ Map whereWasIt = new LinkedHashMap();
+ getVhdFromJndi(whereWasIt);
+ getVhdFromSystemProperties(whereWasIt);
+ getVhdFromBuildProperties(ctx, whereWasIt);
+
+ if (whereWasIt.isEmpty()) {
+ String message = String.format("Can't find a value "
+ + "for the Vitro home directory. "
+ + "Looked in JNDI environment at '%s'. "
+ + "Looked for a system property named '%s'. "
+ + "Looked in 'WEB-INF/resources/build.properties' "
+ + "for '%s'.", VHD_JNDI_PATH, VHD_SYSTEM_PROPERTY,
+ VHD_BUILD_PROPERTY);
+ throw new IllegalStateException(message);
+ } else if (whereWasIt.size() > 1) {
+ String message = String.format("Found multiple values for the "
+ + "Vitro home directory: " + whereWasIt.keySet());
+ ss.warning(this, message);
+ }
+ String message = whereWasIt.keySet().iterator().next();
+ String vhdPath = whereWasIt.values().iterator().next();
+
+ ss.info(this, message);
+
+ File vhd = new File(vhdPath);
+ if (!vhd.exists()) {
+ throw new IllegalStateException("Vitro home directory '" + vhdPath
+ + "' does not exist.");
+ }
+ if (!vhd.isDirectory()) {
+ throw new IllegalStateException("Vitro home directory '" + vhdPath
+ + "' is not a directory.");
+ }
+ if (!vhd.canRead()) {
+ throw new IllegalStateException("Vitro home directory '" + vhdPath
+ + "' cannot be read.");
+ }
+ if (!vhd.canWrite()) {
+ throw new IllegalStateException(
+ "Can't write to Vitro home directory: '" + vhdPath + "'.");
+ }
+
+ return vhd;
+ }
+
+ private void getVhdFromJndi(Map whereWasIt) {
try {
- Context envCtx = (Context) new InitialContext().lookup(JNDI_BASE);
- if (envCtx == null) {
- log.debug("JNDI Lookup on '" + JNDI_BASE + "' failed.");
- return DEFAULT_CONFIG_PATH;
+ String vhdPath = (String) new InitialContext()
+ .lookup(VHD_JNDI_PATH);
+
+ if (vhdPath == null) {
+ log.debug("Didn't find a JNDI value at '" + VHD_JNDI_PATH
+ + "'.");
+ return;
}
- String configPath = (String) envCtx.lookup(PATH_CONFIGURATION);
- if (configPath == null) {
- log.debug("JNDI Lookup on '" + PATH_CONFIGURATION + "' failed.");
- return DEFAULT_CONFIG_PATH;
- }
-
- log.debug("deploy.property as specified by JNDI: " + configPath);
- return configPath;
+ log.debug("'" + VHD_JNDI_PATH + "' as specified by JNDI: "
+ + vhdPath);
+ String message = String.format(
+ "JNDI environment '%s' was set to '%s'", VHD_JNDI_PATH,
+ vhdPath);
+ whereWasIt.put(message, vhdPath);
} catch (NamingException e) {
- log.warn("JNDI lookup failed. "
- + "Using default path for config properties.", e);
- return DEFAULT_CONFIG_PATH;
+ log.debug("JNDI lookup failed. " + e);
}
}
- private boolean resourceExists(String path) {
- return getResourceStream(path) != null;
+ private void getVhdFromSystemProperties(Map whereWasIt) {
+ String vhdPath = System.getProperty(VHD_SYSTEM_PROPERTY);
+
+ if (vhdPath == null) {
+ log.debug("Didn't find a system property value at '"
+ + VHD_SYSTEM_PROPERTY + "'.");
+ return;
+ }
+
+ log.debug("'" + VHD_SYSTEM_PROPERTY
+ + "' as specified by system property: " + vhdPath);
+ String message = String.format("System property '%s' was set to '%s'",
+ VHD_SYSTEM_PROPERTY, vhdPath);
+ whereWasIt.put(message, vhdPath);
}
- private InputStream getResourceStream(String path) {
- return getClass().getClassLoader().getResourceAsStream(path);
- }
+ private void getVhdFromBuildProperties(ServletContext ctx,
+ Map whereWasIt) {
+ String resourcePath = "/WEB-INF/resources/build.properties";
- private boolean externalFileExists(String path) {
- File file = new File(path);
- return file.isFile();
- }
-
- private InputStream getExternalFileStream(String path) {
InputStream stream = null;
- File file = new File(path);
- if (file.isFile()) {
- try {
- stream = new FileInputStream(file);
- } catch (FileNotFoundException e) {
- // testing file.isFile() should have prevented this
- log.error(e, e);
+ try {
+ stream = ctx.getResourceAsStream(resourcePath);
+ if (stream == null) {
+ log.debug("Didn't find a resource at '" + resourcePath + "'.");
+ return;
+ }
+
+ Properties props = new Properties();
+ props.load(stream);
+ String vhdPath = props.getProperty(VHD_BUILD_PROPERTY);
+ if (vhdPath == null) {
+ log.debug("'" + resourcePath + "' didn't contain a value for '"
+ + VHD_BUILD_PROPERTY + "'.");
+ return;
+ }
+
+ log.debug("'" + VHD_BUILD_PROPERTY
+ + "' as specified by build.properties: " + vhdPath);
+ String message = String.format(
+ "In resource '%s', '%s' was set to '%s'.", resourcePath,
+ VHD_BUILD_PROPERTY, vhdPath);
+ whereWasIt.put(message, vhdPath);
+ } catch (IOException e) {
+ log.warn("Failed to load from '" + resourcePath + "'.", e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
- return stream;
+ }
+
+ private File locateRuntimePropertiesFile(File vitroHomeDir, StartupStatus ss) {
+ File rpf = new File(vitroHomeDir, FILE_RUNTIME_PROPERTIES);
+
+ if (!rpf.exists()) {
+ throw new IllegalStateException("Did not find '"
+ + FILE_RUNTIME_PROPERTIES + "' in vitro home directory '"
+ + vitroHomeDir + "'");
+ }
+ if (!rpf.isFile()) {
+ throw new IllegalStateException("'" + rpf.getPath()
+ + "' is not a file.");
+ }
+ if (!rpf.canRead()) {
+ throw new IllegalStateException("Cannot read '" + rpf.getPath()
+ + "'.");
+ }
+ ss.info(this, "Loading runtime properties from '" + rpf.getPath() + "'");
+ return rpf;
+ }
+
+ private Map createPreemptiveProperties(
+ String propertyVitroHome, File vitroHomeDir) {
+ Map map = new HashMap();
+ map.put(propertyVitroHome, vitroHomeDir.getAbsolutePath());
+ return map;
}
@Override
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java
index e5b1018f9..7d7a6211b 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java
@@ -32,7 +32,7 @@ public class ConfigurationPropertiesSmokeTests implements
private static final Log log = LogFactory
.getLog(ConfigurationPropertiesSmokeTests.class);
- private static final String PROPERTY_HOME_DIRECTORY = "vitro.home.directory";
+ private static final String PROPERTY_HOME_DIRECTORY = "vitro.home";
private static final String PROPERTY_DB_URL = "VitroConnection.DataSource.url";
private static final String PROPERTY_DB_USERNAME = "VitroConnection.DataSource.username";
private static final String PROPERTY_DB_PASSWORD = "VitroConnection.DataSource.password";
@@ -61,7 +61,7 @@ public class ConfigurationPropertiesSmokeTests implements
ConfigurationProperties props, StartupStatus ss) {
String homeDirectoryPath = props.getProperty(PROPERTY_HOME_DIRECTORY);
if (homeDirectoryPath == null || homeDirectoryPath.isEmpty()) {
- ss.fatal(this, "deploy.properties does not contain a value for '"
+ ss.fatal(this, "Can't find a value for the home directory: '"
+ PROPERTY_HOME_DIRECTORY + "'");
return;
}
@@ -97,19 +97,19 @@ public class ConfigurationPropertiesSmokeTests implements
ConfigurationProperties props, StartupStatus ss) {
String url = props.getProperty(PROPERTY_DB_URL);
if (url == null || url.isEmpty()) {
- ss.fatal(this, "deploy.properties does not contain a value for '"
+ ss.fatal(this, "runtime.properties does not contain a value for '"
+ PROPERTY_DB_URL + "'");
return;
}
String username = props.getProperty(PROPERTY_DB_USERNAME);
if (username == null || username.isEmpty()) {
- ss.fatal(this, "deploy.properties does not contain a value for '"
+ ss.fatal(this, "runtime.properties does not contain a value for '"
+ PROPERTY_DB_USERNAME + "'");
return;
}
String password = props.getProperty(PROPERTY_DB_PASSWORD);
if (password == null || password.isEmpty()) {
- ss.fatal(this, "deploy.properties does not contain a value for '"
+ ss.fatal(this, "runtime.properties does not contain a value for '"
+ PROPERTY_DB_PASSWORD + "'");
return;
}
@@ -259,7 +259,7 @@ public class ConfigurationPropertiesSmokeTests implements
ConfigurationProperties props, StartupStatus ss) {
String ns = props.getProperty(PROPERTY_DEFAULT_NAMESPACE);
if (ns == null || ns.isEmpty()) {
- ss.fatal(this, "deploy.properties does not contain a value for '"
+ ss.fatal(this, "runtime.properties does not contain a value for '"
+ PROPERTY_DEFAULT_NAMESPACE + "'");
return;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/OntologyController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/OntologyController.java
index da86839ca..6d85141e2 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/OntologyController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/OntologyController.java
@@ -33,9 +33,6 @@ import edu.cornell.mannlib.vitro.webapp.web.ContentType;
public class OntologyController extends VitroHttpServlet{
private static final Log log = LogFactory.getLog(OntologyController.class.getName());
- private String default_jsp = Controllers.BASIC_JSP;
- private ApplicationBean appBean;
-
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException{
doGet(request, response);
@@ -242,7 +239,7 @@ public class OntologyController extends VitroHttpServlet{
throws IOException, ServletException {
VitroRequest vreq = new VitroRequest(req);
- ApplicationBean appBean = ApplicationBean.getAppBean(getServletContext());
+ ApplicationBean appBean = vreq.getAppBean();
//set title before we do the highlighting so we don't get markup in it.
req.setAttribute("title","not found");
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 7e67e0886..88416f911 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java
@@ -283,12 +283,8 @@ public class VitroRequest extends HttpServletRequestWrapper {
}
public ApplicationBean getAppBean(){
- //return (ApplicationBean) getAttribute("appBean");
return getWebappDaoFactory().getApplicationDao().getApplicationBean();
}
- public void setAppBean(ApplicationBean ab){
- setAttribute("appBean",ab);
- }
@SuppressWarnings("unchecked")
@Override
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java
index 2b5866b5a..fb587eb20 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java
@@ -7,7 +7,6 @@ import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
-import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
@@ -31,7 +30,7 @@ public class StartupStatusController extends FreemarkerHttpServlet {
body.put("title", "Startup Status");
body.put("status", StartupStatus.getBean(getServletContext()));
body.put("contextPath", getContextPath());
- body.put("applicationName", getApplicationName());
+ body.put("applicationName", getApplicationName(vreq));
return new TemplateResponseValues("startupStatus-display.ftl", body);
}
@@ -45,11 +44,10 @@ public class StartupStatusController extends FreemarkerHttpServlet {
}
}
- private Object getApplicationName() {
+ private Object getApplicationName(VitroRequest vreq) {
String name = "";
try {
- ApplicationBean app = ApplicationBean.getAppBean(getServletContext());
- name = app.getApplicationName();
+ name = vreq.getAppBean().getApplicationName();
} catch (Exception e) {
// deal with problems below
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java
index 74168a2a3..d1bdf0dfe 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java
@@ -117,7 +117,7 @@ public class ExternalAuthHelper {
}
if (externalAuthServerUrl == null) {
- log.debug("deploy.properties doesn't contain a value for '"
+ log.debug("runtime.properties doesn't contain a value for '"
+ PROPERTY_EXTERNAL_AUTH_SERVER_URL
+ "' -- sending directly to '" + returnUrl + "'");
return returnUrl;
@@ -142,7 +142,7 @@ public class ExternalAuthHelper {
if (externalAuthHeaderName == null) {
log.error("User asked for external authentication, "
- + "but deploy.properties doesn't contain a value for '"
+ + "but runtime.properties doesn't contain a value for '"
+ PROPERTY_EXTERNAL_AUTH_ID_HEADER + "'");
return null;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
index a2e77115a..878a60b30 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
@@ -37,7 +37,7 @@ public class LoginExternalAuthReturn extends BaseLoginServlet {
* the header will contain the name of the user who just logged in.
*
* Deal with these possibilities:
- * - The header name was not configured in deploy.properties. Complain.
+ * - The header name was not configured in runtime.properties. Complain.
* - No username: the login failed. Complain
* - User corresponds to a User acocunt. Record the login.
* - User corresponds to an Individual (self-editor).
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java
index 96243ff0f..a48d040f0 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java
@@ -50,7 +50,7 @@ public class ContactMailController extends FreemarkerHttpServlet {
private final static String TEMPLATE_ERROR = "contactForm-error.ftl";
private final static String TEMPLATE_FORM = "contactForm-form.ftl";
- private static final String PROPERTY_VITRO_HOME_DIR = "vitro.home.directory";
+ private static final String PROPERTY_VITRO_HOME_DIR = "vitro.home";
private static final String EMAIL_JOURNAL_FILE_DIR = "emailJournal";
private static final String EMAIL_JOURNAL_FILE_NAME = "contactFormEmails.html";
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DelimitingTemplateLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DelimitingTemplateLoader.java
new file mode 100644
index 000000000..a0650366e
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DelimitingTemplateLoader.java
@@ -0,0 +1,126 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import freemarker.cache.TemplateLoader;
+
+/**
+ * Wrap a TemplateLoader, so each time a template is read, delimiters will be
+ * inserted at the beginning and end. This makes it easier for a developer can
+ * see what lines of HTML come from which templates.
+ *
+ * TemplateLoader returns a token object when finding a template, and then
+ * recognizes that object when it is used as an argument to getLastModified() or
+ * getReader(). In order to keep track of the template name, we wrap the token
+ * object and the name in a token of our own.
+ *
+ * Taking the easy way out and reading in the entire template into a string.
+ * This limits the template size to less than 2^31 characters (~2 GBytes). That
+ * seems adequate.
+ */
+public class DelimitingTemplateLoader implements TemplateLoader {
+ private static final Log log = LogFactory
+ .getLog(DelimitingTemplateLoader.class);
+
+ private final TemplateLoader innerLoader;
+
+ public DelimitingTemplateLoader(TemplateLoader innerLoader) {
+ this.innerLoader = innerLoader;
+ }
+
+ @Override
+ public Object findTemplateSource(String name) throws IOException {
+ Object innerTS = innerLoader.findTemplateSource(name);
+ if (innerTS == null) {
+ return null;
+ } else {
+ return new DelimitingTemplateSource(name, innerTS);
+ }
+ }
+
+ @Override
+ public long getLastModified(Object templateSource) {
+ DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
+ return innerLoader.getLastModified(dts.ts);
+ }
+
+ @Override
+ public Reader getReader(Object templateSource, String encoding)
+ throws IOException {
+ DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
+ StringBuilder sb = new StringBuilder();
+ sb.append("");
+ sb.append(readTemplateSource(encoding, dts.ts));
+ sb.append("\n");
+ return new StringReader(sb.toString());
+ }
+
+ private StringBuilder readTemplateSource(String encoding, Object ts)
+ throws IOException {
+ StringBuilder sb = new StringBuilder();
+ Reader reader = innerLoader.getReader(ts, encoding);
+ char[] buffer = new char[8192];
+ int howmany;
+ while (-1 != (howmany = reader.read(buffer))) {
+ sb.append(buffer, 0, howmany);
+ }
+ return sb;
+ }
+
+ @Override
+ public void closeTemplateSource(Object templateSource) throws IOException {
+ DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
+ innerLoader.closeTemplateSource(dts.ts);
+ }
+
+ // ----------------------------------------------------------------------
+ // Helper classes
+ // ----------------------------------------------------------------------
+
+ /**
+ * Data object, wrapping the template name and the templateSource object
+ * from the inner TemplateLoader.
+ */
+ private static class DelimitingTemplateSource {
+ public final String name;
+ public final Object ts;
+
+ public DelimitingTemplateSource(String name, Object ts) {
+ if (name == null) {
+ throw new NullPointerException("name may not be null.");
+ }
+ if (ts == null) {
+ throw new NullPointerException("ts may not be null.");
+ }
+ this.name = name;
+ this.ts = ts;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() ^ ts.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null) {
+ return false;
+ }
+ if (!o.getClass().equals(this.getClass())) {
+ return false;
+ }
+ DelimitingTemplateSource that = (DelimitingTemplateSource) o;
+ return this.name.equals(that.name) && this.ts.equals(that.ts);
+ }
+ }
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java
index 4ef527d1e..c6bc10185 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java
@@ -6,10 +6,14 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -17,8 +21,12 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants;
+import edu.cornell.mannlib.vitro.webapp.i18n.freemarker.I18nMethodModel;
+import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
+import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetterUtils;
import edu.cornell.mannlib.vitro.webapp.web.directives.IndividualShortViewDirective;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod;
@@ -27,30 +35,38 @@ import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
+import freemarker.core.Environment;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModelException;
+import freemarker.template.utility.DeepUnwrap;
public class FreemarkerConfiguration extends Configuration {
private static final Log log = LogFactory.getLog(FreemarkerConfiguration.class);
- private final String themeDir;
+ private static final String PROPERTY_DEVELOPER_DEFEAT_CACHE = "developer.defeatFreemarkerCache";
+ private static final String PROPERTY_DEVELOPER_INSERT_DELIMITERS = "developer.insertFreemarkerDelimiters";
+
+ private final String themeDir;
private final ServletContext context;
private final ApplicationBean appBean;
+ private final ConfigurationProperties props;
FreemarkerConfiguration(String themeDir, ApplicationBean appBean, ServletContext context) {
this.themeDir = themeDir;
this.context = context;
this.appBean = appBean;
-
- String buildEnv = ConfigurationProperties.getBean(context).getProperty("Environment.build");
- log.debug("Current build environment: " + buildEnv);
- if ("development".equals(buildEnv)) { // Set Environment.build = development in deploy.properties
- log.debug("Disabling Freemarker template caching in development build.");
+ this.props = ConfigurationProperties.getBean(context);
+
+ String flag = props.getProperty(PROPERTY_DEVELOPER_DEFEAT_CACHE, "false");
+ if (Boolean.valueOf(flag.trim())) {
+ log.debug("Disabling Freemarker template caching in development build.");
setTemplateUpdateDelay(0); // no template caching in development
} else {
int delay = 60;
@@ -164,6 +180,7 @@ public class FreemarkerConfiguration extends Configuration {
map.put("profileUrl", new IndividualProfileUrlMethod());
map.put("localName", new IndividualLocalNameMethod());
map.put("placeholderImageUrl", new IndividualPlaceholderImageUrlMethod());
+ map.put("i18n", new I18nMethodModel());
return map;
}
@@ -196,7 +213,110 @@ public class FreemarkerConfiguration extends Configuration {
log.error("Error creating template loaders");
}
- return mtl;
+ // Add the ability to add delimiters to the templates, based on
+ // settings.
+ if (Boolean.valueOf(props.getProperty(PROPERTY_DEVELOPER_INSERT_DELIMITERS))) {
+ return new DelimitingTemplateLoader(mtl);
+ } else {
+ return mtl;
+ }
}
+ /**
+ * Override getTemplate(), so we can apply DataGetters to all included
+ * templates.
+ *
+ * This won't work for top-level Templates, since the Environment hasn't
+ * been created yet. When TemplateProcessingHelper creates the Environment,
+ * it must call retrieveAndRunDataGetters() for the top-level Template.
+ */
+ @Override
+ public Template getTemplate(String name, Locale locale, String encoding,
+ boolean parse) throws IOException {
+ Template template = super.getTemplate(name, locale, encoding, parse);
+
+ if (template == null) {
+ log.debug("Template '" + name + "' not found for locale '" + locale + "'.");
+ return template;
+ }
+
+ Environment env = getEnvironment();
+ if (env == null) {
+ log.debug("Not fetching data getters for template '" + template.getName() + "'. No environment.");
+ return template;
+ }
+
+ retrieveAndRunDataGetters(env, template.getName());
+ return template;
+ }
+
+
+ /**
+ * Find the DataGetters for this template, and apply them to the Freemarker
+ * environment.
+ */
+ public static void retrieveAndRunDataGetters(Environment env, String templateName) {
+ HttpServletRequest req = (HttpServletRequest) env.getCustomAttribute("request");
+ VitroRequest vreq = new VitroRequest(req);
+
+ if (dataGettersAlreadyApplied(env, templateName)) {
+ log.debug("DataGetters for '" + templateName+"' have already been applied");
+ return;
+ }
+
+ try {
+ List dgList = DataGetterUtils.getDataGettersForTemplate(
+ vreq, vreq.getDisplayModel(), templateName);
+ log.debug("Retrieved " + dgList.size() + " data getters for template '" + templateName + "'");
+
+ @SuppressWarnings("unchecked")
+ Map dataMap = (Map) DeepUnwrap.permissiveUnwrap(env.getDataModel());
+ for (DataGetter dg : dgList) {
+ applyDataGetter(dg, env, dataMap);
+ }
+ } catch (Exception e) {
+ log.warn(e, e);
+ }
+ }
+
+ /**
+ * Have the DataGetters for this template already been applied to this environment?
+ * If not, record that they are being applied now.
+ */
+ @SuppressWarnings("unchecked")
+ private static boolean dataGettersAlreadyApplied(Environment env, String templateName) {
+ Set names;
+ Object o = env.getCustomAttribute("dataGettersApplied");
+ if (o instanceof Set) {
+ names = (Set) o;
+ } else {
+ names = new HashSet();
+ }
+
+ boolean added = names.add(templateName);
+ if (added) {
+ env.setCustomAttribute("dataGettersApplied", names);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Get the data from a DataGetter, and store it in global variables in the
+ * Freemarker environment.
+ */
+ private static void applyDataGetter(DataGetter dg, Environment env,
+ Map dataMap) throws TemplateModelException {
+ Map moreData = dg.getData(dataMap);
+ ObjectWrapper wrapper = env.getObjectWrapper();
+ if (moreData != null) {
+ for (String key : moreData.keySet()) {
+ Object value = moreData.get(key);
+ env.setGlobalVariable(key, wrapper.wrap(value));
+ log.debug("Stored in environment: '" + key + "' = '" + value + "'");
+ }
+ }
+ }
+
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java
index ffca0bb65..c1fd229f3 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java
@@ -51,12 +51,20 @@ public class TemplateProcessingHelper {
env.setCustomAttribute("request", request);
env.setCustomAttribute("context", context);
+ // Set the Locale from the request into the environment, so date builtins will be
+ // Locale-dependent
+ env.setLocale(request.getLocale());
+
// Define a setup template to be included by every page template
String templateType = (String) map.get("templateType");
if (FreemarkerHttpServlet.PAGE_TEMPLATE_TYPE.equals(templateType)) {
env.include(getTemplate("pageSetup.ftl"));
}
+ // Apply any data-getters that are associated with this template.
+ FreemarkerConfiguration.retrieveAndRunDataGetters(env, template.getName());
+
+ // Now process it.
env.process();
} catch (TemplateException e) {
throw new TemplateProcessingException("TemplateException creating processing environment", e);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java
index fa874eec0..f43113d8a 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java
@@ -5,7 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
-import java.util.HashMap;
+//import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -129,7 +130,7 @@ public class UrlBuilder {
return getUrl(Route.LOGOUT);
}
- public static class ParamMap extends HashMap {
+ public static class ParamMap extends LinkedHashMap {
private static final long serialVersionUID = 1L;
public ParamMap() { }
@@ -276,7 +277,7 @@ public class UrlBuilder {
}
if (profileUrl != null) {
- HashMap specialParams = getModelParams(vreq);
+ LinkedHashMap specialParams = getModelParams(vreq);
if(specialParams.size() != 0) {
profileUrl = addParams(profileUrl, new ParamMap(specialParams));
}
@@ -325,9 +326,9 @@ public class UrlBuilder {
//To be used in different property templates so placing method for reuse here
//Check if special params included, specifically for menu management and other models
- public static HashMap getModelParams(VitroRequest vreq) {
+ public static LinkedHashMap getModelParams(VitroRequest vreq) {
- HashMap specialParams = new HashMap();
+ LinkedHashMap specialParams = new LinkedHashMap();
if(vreq != null) {
//this parameter is sufficient to switch to menu model
String useMenuModelParam = vreq.getParameter(DisplayVocabulary.SWITCH_TO_DISPLAY_MODEL);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java
index 1164b383d..670eeeda3 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java
@@ -201,7 +201,7 @@ public class JSONReconcileServlet extends VitroHttpServlet {
viewJson.put("url", urlBuf.toString() + "/individual?uri={{id}}");
json.put("view", viewJson);
- // parse defaultTypeList from deploy.properties
+ // parse defaultTypeList from runtime.properties
if (defaultTypeList != null) {
String[] splitList = defaultTypeList.split(";");
String[][] idNameArray = new String[splitList.length][splitList.length];
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContext.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContext.java
index f822360cc..96300bbad 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContext.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContext.java
@@ -15,12 +15,6 @@ public interface IndividualRequestAnalysisContext {
*/
String getDefaultNamespace();
- /**
- * Is there a namespace for this prefix? If not, return an empty string, but
- * never null.
- */
- String getNamespaceForPrefix(String prefix);
-
/**
* Use the IndividualDao to get this individual.
*
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContextImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContextImpl.java
index 2198c89f5..5f3ebc7e4 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContextImpl.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalysisContextImpl.java
@@ -15,8 +15,6 @@ import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
-import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
-import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapperFactory;
/**
* Implement all of the fiddly-bits that we need for analyzing the request for
@@ -45,25 +43,6 @@ public class IndividualRequestAnalysisContextImpl implements
return wadf.getDefaultNamespace();
}
- @Override
- public String getNamespaceForPrefix(String prefix) {
- if (prefix == null) {
- return "";
- }
-
- NamespaceMapper namespaceMapper = NamespaceMapperFactory
- .getNamespaceMapper(ctx);
- if (namespaceMapper == null) {
- log.warn("No NamespaceMapper in ServletContext. Request URL was '"
- + vreq.getRequestURL() + "'");
- return "";
- }
-
- String ns = namespaceMapper.getNamespaceForPrefix(prefix);
-
- return (ns == null) ? "" : ns;
- }
-
@Override
public Individual getIndividualByURI(String individualUri) {
if (individualUri == null) {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalyzer.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalyzer.java
index 2d5bad4a3..3b3c6d091 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalyzer.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRequestAnalyzer.java
@@ -30,7 +30,6 @@ public class IndividualRequestAnalyzer {
private static Pattern RDF_REQUEST = Pattern.compile("^/individual/([^/]+)/\\1\\.(rdf|n3|ttl)$");
private static Pattern HTML_REQUEST = Pattern.compile("^/display/([^/]+)$");
private static Pattern LINKED_DATA_URL = Pattern.compile("^/individual/([^/]+)$");
- private static Pattern NS_PREFIX_URL = Pattern.compile("^/individual/([^/]*)/([^/]+)$");
private final VitroRequest vreq;
private final IndividualRequestAnalysisContext analysisContext;
@@ -164,7 +163,6 @@ public class IndividualRequestAnalyzer {
* /individual/localname/localname.rdf
* /individual/localname/localname.n3
* /individual/localname/localname.ttl
- * /individual/nsprefix/localname
*
*
* @return null on failure.
@@ -202,14 +200,6 @@ public class IndividualRequestAnalyzer {
return getIndividualByLocalname(rdfMatch.group(1));
}
- // Does the URL look like a namespace prefix followed by a local
- // name?
- Matcher prefix_match = NS_PREFIX_URL.matcher(url);
- if (prefix_match.matches() && prefix_match.groupCount() == 2) {
- return getIndividualByPrefixAndLocalname(prefix_match.group(1),
- prefix_match.group(2));
- }
-
// Couldn't match it to anything.
return null;
} catch (Throwable e) {
@@ -299,12 +289,6 @@ public class IndividualRequestAnalyzer {
return getIndividualByUri(uri);
}
- private Individual getIndividualByPrefixAndLocalname(String prefix,
- String localName) {
- String ns = analysisContext.getNamespaceForPrefix(prefix);
- return getIndividualByUri(ns + localName);
- }
-
private Individual getIndividualByNetId(String netId) {
return analysisContext.getIndividualByNetId(netId);
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java
index a70b56ec2..ac39c3b10 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java
@@ -82,6 +82,7 @@ class IndividualResponseBuilder {
body.put("relatedSubject", getRelatedSubject());
body.put("namespaces", namespaces);
body.put("temporalVisualizationEnabled", getTemporalVisualizationFlag());
+ body.put("profilePageTypesEnabled", getprofilePageTypesFlag());
body.put("verbosePropertySwitch", getVerbosePropertyValues());
//Execute data getters that might apply to this individual, e.g. because of the class of the individual
@@ -91,6 +92,13 @@ class IndividualResponseBuilder {
log.error("Data retrieval for individual lead to error", ex);
}
+ // for quick profile view - users can toggle between the quick and the full views,
+ // so the "destination" let's us know which view they are targeting. On normal
+ // page request, this string is empty and the default template is loaded.
+ String targetedView = "";
+ targetedView = vreq.getParameter("destination");
+ body.put("targetedView", targetedView);
+
//Individual template model
IndividualTemplateModel itm = getIndividualTemplateModel(individual);
/* We need to expose non-getters in displaying the individual's property list,
@@ -100,6 +108,7 @@ class IndividualResponseBuilder {
*/
// body.put("individual", wrap(itm, BeansWrapper.EXPOSE_SAFE));
body.put("labelCount", getLabelCount(itm.getUri(), vreq));
+ body.put("profileType", getProfileType(itm.getUri(), vreq));
body.put("individual", wrap(itm, new ReadOnlyBeansWrapper()));
body.put("headContent", getRdfLinkTag(itm));
@@ -169,6 +178,12 @@ class IndividualResponseBuilder {
return "enabled".equals(property);
}
+ private boolean getprofilePageTypesFlag() {
+ String property = ConfigurationProperties.getBean(vreq).getProperty(
+ "MultiViews.profilePageTypes");
+ return "enabled".equals(property);
+ }
+
private Map getVerbosePropertyValues() {
Map map = null;
@@ -275,4 +290,29 @@ class IndividualResponseBuilder {
}
return theCount;
}
+
+ private static String PROFILE_TYPE_QUERY = ""
+ + "PREFIX display: \n"
+ + "SELECT ?profile WHERE { \n"
+ + " ?subject display:hasDefaultProfilePageType ?profile \n"
+ + "}" ;
+
+ private static String getProfileType(String subjectUri, VitroRequest vreq) {
+ String queryStr = QueryUtils.subUriForQueryVar(PROFILE_TYPE_QUERY, "subject", subjectUri);
+ log.debug("queryStr = " + queryStr);
+ String profileType = "none";
+ try {
+ ResultSet results = QueryUtils.getQueryResults(queryStr, vreq);
+ if (results.hasNext()) {
+ QuerySolution soln = results.nextSolution();
+ String profileStr = soln.get("profile").toString();
+ if ( profileStr.length() > 0 ) {
+ profileType = profileStr.substring(profileStr.indexOf("#")+1,profileStr.length());
+ }
+ }
+ } catch (Exception e) {
+ log.error(e, e);
+ }
+ return profileType;
+ }
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetRenderedSolrIndividualsByVClass.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetRenderedSolrIndividualsByVClass.java
index 5e693ee5e..81d2ede9e 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetRenderedSolrIndividualsByVClass.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetRenderedSolrIndividualsByVClass.java
@@ -2,7 +2,9 @@
package edu.cornell.mannlib.vitro.webapp.controller.json;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
@@ -24,7 +26,7 @@ import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.Individual
* Does a Solr search for individuals, and uses the short view to render each of
* the results.
*/
-public class GetRenderedSolrIndividualsByVClass extends JsonObjectProducer {
+public class GetRenderedSolrIndividualsByVClass extends GetSolrIndividualsByVClasses {
private static final Log log = LogFactory
.getLog(GetRenderedSolrIndividualsByVClass.class);
@@ -33,21 +35,34 @@ public class GetRenderedSolrIndividualsByVClass extends JsonObjectProducer {
}
/**
- * Search for individuals by VClass. The class URI and the paging
+ * Search for individuals by VClass or VClasses in the case of multiple parameters. The class URI(s) and the paging
* information are in the request parameters.
*/
@Override
protected JSONObject process() throws Exception {
JSONObject rObj = null;
- VClass vclass = getVclassParameter(vreq);
- String vclassId = vclass.getURI();
-
+
+ //This gets the first vclass value and sets that as display type
+ List vclassIds = super.getVclassIds(vreq);
+ String vclassId = null;
+ if(vclassIds.size() > 1) {
+ //This ensures the second class instead of the first
+ //This is a temporary fix in cases where institutional internal classes are being sent in
+ //and thus the second class is the actual class with display information associated
+ vclassId = vclassIds.get(1);
+ } else {
+ vclassId = vclassIds.get(0);
+ }
vreq.setAttribute("displayType", vclassId);
- rObj = JsonServlet.getSolrIndividualsByVClass(vclassId, vreq, ctx);
+
+ //This will get all the solr individuals by VClass (if one value) or the intersection
+ //i.e. individuals that have all the types for the different vclasses entered
+ rObj = super.process();
addShortViewRenderings(rObj);
-
return rObj;
}
+
+ //Get
/**
* Look through the return object. For each individual, render the short
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelper.java
deleted file mode 100644
index eaa83f396..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelper.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.controller.login;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
-import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
-import edu.cornell.mannlib.vitro.webapp.controller.edit.Authenticate;
-import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
-import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException;
-import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
-import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.State;
-
-/**
- * A temporary means of displaying the Login templates within the SiteAdmin
- * form.
- *
- * This class contains stuff that I swiped from {@link Authenticate}. The base
- * class, {@link LoginTemplateHelperBase}, contains stuff that I swiped from
- * {@link FreemarkerHttpServlet}.
- */
-public class LoginTemplateHelper extends LoginTemplateHelperBase {
- private static final Log log = LogFactory.getLog(LoginTemplateHelper.class);
-
- /** If they are logging in, show them this form. */
- public static final String TEMPLATE_LOGIN = "login-form.ftl";
-
- /** If they are changing their password on first login, show them this form. */
- public static final String TEMPLATE_FORCE_PASSWORD_CHANGE = "login-forcedPasswordChange.ftl";
-
- /** Show error message */
- public static final String TEMPLATE_SERVER_ERROR = Template.ERROR_MESSAGE.toString();
-
- public static final String BODY_LOGIN_NAME = "loginName";
- public static final String BODY_FORM_ACTION = "formAction";
- public static final String BODY_INFO_MESSAGE = "infoMessage";
- public static final String BODY_ERROR_MESSAGE = "errorMessage";
- public static final String BODY_CANCEL_URL = "cancelUrl";
-
- public LoginTemplateHelper(HttpServletRequest req) {
- super(req);
- }
-
- /** Version for JSP page */
- public String showLoginPage(HttpServletRequest request) {
- VitroRequest vreq = new VitroRequest(request);
- try {
- State state = getCurrentLoginState(vreq);
- log.debug("State on exit: " + state);
-
- switch (state) {
- case LOGGED_IN:
- return "";
- case FORCED_PASSWORD_CHANGE:
- return doTemplate(vreq, showPasswordChangeScreen(vreq));
- default:
- return doTemplate(vreq, showLoginScreen(vreq));
- }
- } catch (TemplateProcessingException e) {
- log.error(e.getMessage(), e);
- return null;
- } catch (Exception e) {
- log.error(e, e);
- try {
- return doTemplate(vreq, showError(e));
- } catch (TemplateProcessingException e1) {
- log.error(e1.getMessage(), e1);
- return null;
- }
- }
- }
-
- /** Version for Freemarker page */
- public TemplateResponseValues showLoginPanel(VitroRequest vreq) {
- try {
-
- State state = getCurrentLoginState(vreq);
- log.debug("State on exit: " + state);
-
- switch (state) {
- // RY Why does this case exist? We don't call this method if a user is logged in.
- case LOGGED_IN:
- return null;
- case FORCED_PASSWORD_CHANGE:
- // return doTemplate(vreq, showPasswordChangeScreen(vreq), body, config);
- return showPasswordChangeScreen(vreq);
- default:
- // return doTemplate(vreq, showLoginScreen(vreq), body, config);
- return showLoginScreen(vreq);
- }
- } catch (Exception e) {
- log.error(e, e);
- return showError(e);
- }
- }
-
- /**
- * User is just starting the login process. Be sure that we have a
- * {@link LoginProcessBean} with the correct status. Show them the login
- * screen.
- */
- private TemplateResponseValues showLoginScreen(VitroRequest vreq)
- throws IOException {
- LoginProcessBean bean = LoginProcessBean.getBean(vreq);
- bean.setState(State.LOGGING_IN);
- log.trace("Going to login screen: " + bean);
-
- TemplateResponseValues trv = new TemplateResponseValues(TEMPLATE_LOGIN);
- trv.put(BODY_FORM_ACTION, getAuthenticateUrl(vreq));
- trv.put(BODY_LOGIN_NAME, bean.getUsername());
-
- String infoMessage = bean.getInfoMessageAndClear();
- if (!infoMessage.isEmpty()) {
- trv.put(BODY_INFO_MESSAGE, infoMessage);
- }
- String errorMessage = bean.getErrorMessageAndClear();
- if (!errorMessage.isEmpty()) {
- trv.put(BODY_ERROR_MESSAGE, errorMessage);
- }
-
- return trv;
- }
-
- /**
- * The user has given the correct password, but now they are required to
- * change it (unless they cancel out).
- */
- private TemplateResponseValues showPasswordChangeScreen(VitroRequest vreq) {
- LoginProcessBean bean = LoginProcessBean.getBean(vreq);
- bean.setState(State.FORCED_PASSWORD_CHANGE);
- log.trace("Going to password change screen: " + bean);
-
- TemplateResponseValues trv = new TemplateResponseValues(
- TEMPLATE_FORCE_PASSWORD_CHANGE);
- trv.put(BODY_FORM_ACTION, getAuthenticateUrl(vreq));
- trv.put(BODY_CANCEL_URL, getCancelUrl(vreq));
-
- String errorMessage = bean.getErrorMessageAndClear();
- if (!errorMessage.isEmpty()) {
- trv.put(BODY_ERROR_MESSAGE, errorMessage);
- }
- return trv;
- }
-
- private TemplateResponseValues showError(Exception e) {
- TemplateResponseValues trv = new TemplateResponseValues(
- TEMPLATE_SERVER_ERROR);
- trv.put(BODY_ERROR_MESSAGE, "Internal server error: " + e);
- return trv;
- }
-
- /**
- * We processed a response, and want to show a template. Version for JSP
- * page.
- */
- private String doTemplate(VitroRequest vreq, TemplateResponseValues values) throws TemplateProcessingException {
- // Set it up like FreeMarkerHttpServlet.doGet() would do.
- Map map = new HashMap();
- map.putAll(getPageTemplateValues(vreq));
- map.putAll(values.getMap());
-
- return processTemplateToString(values.getTemplateName(), map, vreq);
- }
-
- /**
- * Where are we in the process? Logged in? Not? Somewhere in between?
- */
- private State getCurrentLoginState(HttpServletRequest request) {
- if (LoginStatusBean.getBean(request).isLoggedIn()) {
- return State.LOGGED_IN;
- } else {
- return LoginProcessBean.getBean(request).getState();
- }
- }
-
- /** What's the URL for this servlet? */
- private String getAuthenticateUrl(HttpServletRequest request) {
- String contextPath = request.getContextPath();
- String urlParams = "?login=block";
- return contextPath + "/authenticate" + urlParams;
- }
-
- /** What's the URL for this servlet, with the cancel parameter added? */
- private String getCancelUrl(HttpServletRequest request) {
- String contextPath = request.getContextPath();
- String urlParams = "?login=block&cancel=true";
- return contextPath + "/authenticate" + urlParams;
- }
-}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelperBase.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelperBase.java
deleted file mode 100644
index 732aa74c2..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginTemplateHelperBase.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.controller.login;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-
-import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
-
-/**
- * A temporary means of displaying the Login templates within the SiteAdmin
- * form.
- *
- * The constructor insures that the ServletContext is set.
- */
-public class LoginTemplateHelperBase extends FreemarkerHttpServlet {
- private final ServletContext servletContext;
-
- LoginTemplateHelperBase(HttpServletRequest req) {
- this.servletContext = req.getSession().getServletContext();
- }
-
- public ServletContext getServletContext() {
- return servletContext;
- }
-
-}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java
index 7b7ec4281..388a69078 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java
@@ -438,13 +438,17 @@ public class IndividualDaoSDB extends IndividualDaoJena {
final List list =
new LinkedList();
- // get all labeled resources from any non-tbox and non-metadata graphs.
+ // get all labeled resources from any non-tbox and non-metadata graphs,
+ // as well as the unnamed graph (first pattern below)
String query = "SELECT DISTINCT ?ind WHERE { \n" +
+ " { ?ind <" + RDFS.label.getURI() + "> ?label } " +
+ " UNION { " +
" GRAPH ?g { ?ind <" + RDFS.label.getURI() +
"> ?label } \n" +
" FILTER (?g != <" + JenaDataSourceSetupBase
.JENA_APPLICATION_METADATA_MODEL + "> " +
" && !regex(str(?g),\"tbox\")) \n " +
+ " } " +
"}";
Query q = QueryFactory.create(query);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java
index c2467f412..a12bd2fbe 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java
@@ -848,14 +848,19 @@ public class IndividualSDB extends IndividualImpl implements Individual {
String getTypes =
"CONSTRUCT{ <" + this.individualURI + "> <" + RDF.type +
"> ?types }\n" +
- "WHERE{ GRAPH ?g"
+ "WHERE{ { GRAPH ?g"
+ " { <" + this.individualURI +"> <" +RDF.type+ "> ?types } \n"
+ WebappDaoFactorySDB.getFilterBlock(
graphVars, (direct
? WebappDaoFactorySDB.SDBDatasetMode
.ASSERTIONS_ONLY
: datasetMode))
- + "} \n";
+ + "} \n"
+ // GRAPH-less pattern to support retrieving inferred types
+ // from the unnamed base graph, as in Sesame and OWLIM
+ + ((datasetMode.equals(WebappDaoFactorySDB.SDBDatasetMode.ASSERTIONS_ONLY))
+ ? "" : "UNION { <" + this.individualURI +"> <" +RDF.type+ "> ?types }" )
+ + "} \n";
RDFService service = webappDaoFactory.getRDFService();
try {
tempModel = RDFServiceUtils.parseModel(
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoSDB.java
index 3ce03edca..c177161fa 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoSDB.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassDaoSDB.java
@@ -139,10 +139,8 @@ public class VClassDaoSDB extends VClassDaoJena {
try {
String queryText =
"SELECT COUNT( DISTINCT ?instance ) WHERE { \n" +
- " GRAPH { \n" +
" ?class <"+VitroVocabulary.IN_CLASSGROUP+"> <"+vcg.getURI() +"> .\n" +
" ?instance a ?class . \n" +
- " } \n" +
"} \n" ;
Query countQuery = QueryFactory.create(queryText, Syntax.syntaxARQ);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java
index fd9ff67b1..7668d41e8 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java
@@ -66,16 +66,29 @@ public class IndividualsViaObjectPropetyOptions implements FieldOptions {
Individual subject = wDaoFact.getIndividualDao().getIndividualByURI(subjectUri);
ObjectProperty objProp = wDaoFact.getObjectPropertyDao().getObjectPropertyByURI(predicateUri);
- List vclasses = wDaoFact.getVClassDao().getVClassesForProperty(subject.getVClassURI(), predicateUri);
+ //get all vclasses applicable to the individual subject
+ List subjectVClasses = subject.getVClasses();
+ //using hashset to prevent duplicates
+ HashSet vclassesURIs = new HashSet();
+ //Get the range vclasses applicable for the property and each vclass for the subject
+ for(VClass subjectVClass: subjectVClasses) {
+ List vclasses = wDaoFact.getVClassDao().getVClassesForProperty(subjectVClass.getURI(), predicateUri);
+ //add range vclass to hash
+ if(vclasses != null) {
+ for(VClass v: vclasses) {
+ vclassesURIs.add(v.getURI());
+ }
+ }
+ }
- if (vclasses == null || vclasses.size() == 0) {
+ if (vclassesURIs.size() == 0) {
return optionsMap;
}
List individuals = new ArrayList();
HashSet uriSet = new HashSet();
- for (VClass vclass : vclasses) {
- List inds = wDaoFact.getIndividualDao().getIndividualsByVClassURI(vclass.getURI(), -1, -1);
+ for (String vclassURI: vclassesURIs) {
+ List inds = wDaoFact.getIndividualDao().getIndividualsByVClassURI(vclassURI, -1, -1);
for (Individual ind : inds) {
if (!uriSet.contains(ind.getURI())) {
uriSet.add(ind.getURI());
@@ -88,7 +101,7 @@ public class IndividualsViaObjectPropetyOptions implements FieldOptions {
individuals = removeIndividualsAlreadyInRange(
individuals, stmts, predicateUri, objectUri);
- // Collections.sort(individuals,new compareIndividualsByName());
+ // Collections.sort(individuals,new compareIndividualsByName());a
for (Individual ind : individuals) {
String uri = ind.getURI();
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeIntervalFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeIntervalFormGenerator.java
index 94063a46b..22c8fcb5c 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeIntervalFormGenerator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeIntervalFormGenerator.java
@@ -39,7 +39,17 @@ public class DateTimeIntervalFormGenerator extends
public EditConfigurationVTwo getEditConfiguration(VitroRequest vreq,
HttpSession session) {
EditConfigurationVTwo conf = new EditConfigurationVTwo();
+ this.setupEditConfiguration(conf, vreq, session);
+ //Prepare
+ prepare(vreq, conf);
+ return conf;
+ }
+
+ //Enables refactoring by other generators, as this code can be used to partially setup an
+ //edit configuration object which can then be extended - prepare can then be called independently
+ //enabling sparql queries to be evaluated using the final edit configuration object
+ public void setupEditConfiguration(EditConfigurationVTwo conf, VitroRequest vreq, HttpSession session) {
initBasics(conf, vreq);
initPropertyParameters(vreq, session, conf);
initObjectPropForm(conf, vreq);
@@ -48,11 +58,11 @@ public class DateTimeIntervalFormGenerator extends
conf.setVarNameForSubject("subject");
conf.setVarNameForPredicate("toDateTimeInterval");
- conf.setVarNameForObject("intervalNode");
+ conf.setVarNameForObject(getNodeVar());
- conf.setN3Optional(Arrays.asList(n3ForStart, n3ForEnd));
+ conf.setN3Optional(n3ForStart, n3ForEnd);
- conf.addNewResource("intervalNode", DEFAULT_NS_FOR_NEW_RESOURCE);
+ conf.addNewResource(getNodeVar(), DEFAULT_NS_FOR_NEW_RESOURCE);
conf.addNewResource("startNode", DEFAULT_NS_FOR_NEW_RESOURCE);
conf.addNewResource("endNode", DEFAULT_NS_FOR_NEW_RESOURCE);
@@ -61,7 +71,7 @@ public class DateTimeIntervalFormGenerator extends
conf.addSparqlForExistingLiteral(
"endField-value", existingEndDateQuery);
conf.addSparqlForExistingUris(
- "intervalNode", existingIntervalNodeQuery);
+ getNodeVar(), existingIntervalNodeQuery);
conf.addSparqlForExistingUris("startNode", existingStartNodeQuery);
conf.addSparqlForExistingUris("endNode", existingEndNodeQuery);
conf.addSparqlForExistingUris(
@@ -85,79 +95,87 @@ public class DateTimeIntervalFormGenerator extends
conf.addValidator(new DateTimeIntervalValidationVTwo("startField","endField"));
//Adding additional data, specifically edit mode
addFormSpecificData(conf, vreq);
- //Prepare
- prepare(vreq, conf);
- return conf;
-
}
- final static String n3ForStart =
- "?subject <" + toDateTimeInterval + "> ?intervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToStart + "> ?startNode . \n" +
+ final String n3ForStart =
+ "?subject <" + getToDateTimeIntervalPredicate() + "> " + getNodeN3Var() + " . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToStart + "> ?startNode . \n" +
"?startNode a <" + dateTimeValueType + "> . \n" +
"?startNode <" + dateTimeValue + "> ?startField-value . \n" +
"?startNode <" + dateTimePrecision + "> ?startField-precision . \n";
- final static String n3ForEnd =
- "?subject <" + toDateTimeInterval + "> ?intervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToEnd + "> ?endNode . \n" +
+ final String n3ForEnd =
+ "?subject <" + getToDateTimeIntervalPredicate() + "> " + getNodeN3Var() + " . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToEnd + "> ?endNode . \n" +
"?endNode a <" + dateTimeValueType + "> . \n" +
"?endNode <" + dateTimeValue + "> ?endField-value . \n" +
"?endNode <" + dateTimePrecision + "> ?endField-precision .";
- final static String existingStartDateQuery =
+ final String existingStartDateQuery =
"SELECT ?existingDateStart WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToStart + "> ?startNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToStart + "> ?startNode . \n" +
"?startNode a <" + dateTimeValueType + "> . \n" +
"?startNode <" + dateTimeValue + "> ?existingDateStart }";
- final static String existingEndDateQuery =
+ final String existingEndDateQuery =
"SELECT ?existingEndDate WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToEnd + "> ?endNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToEnd + "> ?endNode . \n" +
"?endNode a <" + dateTimeValueType + "> . \n " +
"?endNode <" + dateTimeValue + "> ?existingEndDate . }";
- final static String existingIntervalNodeQuery =
+ final String existingIntervalNodeQuery =
"SELECT ?existingIntervalNode WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
"?existingIntervalNode a <" + intervalType + "> . }";
- final static String existingStartNodeQuery =
+ final String existingStartNodeQuery =
"SELECT ?existingStartNode WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToStart + "> ?existingStartNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToStart + "> ?existingStartNode . \n" +
"?existingStartNode a <" + dateTimeValueType + "> .} ";
- final static String existingEndNodeQuery =
+ final String existingEndNodeQuery =
"SELECT ?existingEndNode WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToEnd + "> ?existingEndNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToEnd + "> ?existingEndNode . \n" +
"?existingEndNode a <" + dateTimeValueType + "> .} ";
- final static String existingStartPrecisionQuery =
+ final String existingStartPrecisionQuery =
"SELECT ?existingStartPrecision WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToStart + "> ?startNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToStart + "> ?startNode . \n" +
"?startNode a <" + dateTimeValueType + "> . \n" +
"?startNode <" + dateTimePrecision + "> ?existingStartPrecision . }";
- final static String existingEndPrecisionQuery =
+ final String existingEndPrecisionQuery =
"SELECT ?existingEndPrecision WHERE { \n" +
- "?subject <" + toDateTimeInterval + "> ?existingIntervalNode . \n" +
- "?intervalNode a <" + intervalType + "> . \n" +
- "?intervalNode <" + intervalToEnd + "> ?endNode . \n" +
+ "?subject <" + getToDateTimeIntervalPredicate() + "> ?existingIntervalNode . \n" +
+ getNodeN3Var() + " a <" + intervalType + "> . \n" +
+ getNodeN3Var() + " <" + intervalToEnd + "> ?endNode . \n" +
"?endNode a <" + dateTimeValueType + "> . \n" +
"?endNode <" + dateTimePrecision + "> ?existingEndPrecision . }";
+ public String getToDateTimeIntervalPredicate() {
+ return toDateTimeInterval;
+ }
+
+ public String getNodeVar() {
+ return "intervalNode";
+ }
+
+ public String getNodeN3Var() {
+ return "?" + getNodeVar();
+ }
+
//Adding form specific data such as edit mode
public void addFormSpecificData(EditConfigurationVTwo editConfiguration, VitroRequest vreq) {
HashMap formSpecificData = new HashMap();
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeValueFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeValueFormGenerator.java
index d8edd2ddc..ebb7b298e 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeValueFormGenerator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DateTimeValueFormGenerator.java
@@ -27,7 +27,7 @@ public class DateTimeValueFormGenerator extends BaseEditConfigurationGenerator
implements EditConfigurationGenerator {
final static String vivoCore = "http://vivoweb.org/ontology/core#";
- final static String toDateTimeValue = vivoCore + "dateTimeValue";
+ final String toDateTimeValue = vivoCore + "dateTimeValue";
final static String valueType = vivoCore + "DateTimeValue";
final static String dateTimeValue = vivoCore + "dateTime";
final static String dateTimePrecision = vivoCore + "dateTimePrecision";
@@ -41,23 +41,23 @@ public class DateTimeValueFormGenerator extends BaseEditConfigurationGenerator
initPropertyParameters(vreq, session, conf);
initObjectPropForm(conf, vreq);
- conf.setTemplate("dateTimeValueForm.ftl");
+ conf.setTemplate(this.getTemplate());
conf.setVarNameForSubject("subject");
conf.setVarNameForPredicate("toDateTimeValue");
conf.setVarNameForObject("valueNode");
- conf.setN3Optional(Arrays.asList(n3ForValue));
+ conf.setN3Optional(Arrays.asList(getN3ForValue()));
conf.addNewResource("valueNode", DEFAULT_NS_FOR_NEW_RESOURCE);
conf.addSparqlForExistingLiteral(
- "dateTimeField-value", existingDateTimeValueQuery);
+ "dateTimeField-value", getExistingDateTimeValueQuery());
conf.addSparqlForExistingUris(
- "dateTimeField-precision", existingPrecisionQuery);
- conf.addSparqlForExistingUris("valueNode", existingNodeQuery);
+ "dateTimeField-precision", getExistingPrecisionQuery());
+ conf.addSparqlForExistingUris("valueNode", getExistingNodeQuery());
- FieldVTwo dateTimeField = new FieldVTwo().setName("dateTimeField");
+ FieldVTwo dateTimeField = new FieldVTwo().setName(this.getDateTimeFieldName());
dateTimeField.setEditElement(new DateTimeWithPrecisionVTwo(dateTimeField,
VitroVocabulary.Precision.SECOND.uri(),
VitroVocabulary.Precision.NONE.uri()));
@@ -67,34 +67,61 @@ public class DateTimeValueFormGenerator extends BaseEditConfigurationGenerator
//Adding additional data, specifically edit mode
addFormSpecificData(conf, vreq);
//prepare
- prepare(vreq, conf);
- return conf;
- }
+ prepare(vreq, conf);
+ return conf;
+ }
- final static String n3ForValue =
- "?subject <" + toDateTimeValue + "> ?valueNode . \n" +
+
+ //Writing these as methods instead of static strings allows the method getToDateTimeValuePredicate
+ //to be called after the class has been initialized - this is important for subclasses of this generator
+ //that rely on vreq for predicate
+ protected String getN3ForValue() {
+ return "?subject <" + this.getToDateTimeValuePredicate() + "> ?valueNode . \n" +
"?valueNode a <" + valueType + "> . \n" +
"?valueNode <" + dateTimeValue + "> ?dateTimeField-value . \n" +
- "?valueNode <" + dateTimePrecision + "> ?dateTimeField-precision .";
+ "?valueNode <" + dateTimePrecision + "> ?dateTimeField-precision .";
+ }
- final static String existingDateTimeValueQuery =
- "SELECT ?existingDateTimeValue WHERE { \n" +
- "?subject <" + toDateTimeValue + "> ?existingValueNode . \n" +
+ protected String getExistingDateTimeValueQuery () {
+ return "SELECT ?existingDateTimeValue WHERE { \n" +
+ "?subject <" + this.getToDateTimeValuePredicate() + "> ?existingValueNode . \n" +
"?existingValueNode a <" + valueType + "> . \n" +
"?existingValueNode <" + dateTimeValue + "> ?existingDateTimeValue }";
+ }
- final static String existingPrecisionQuery =
- "SELECT ?existingPrecision WHERE { \n" +
- "?subject <" + toDateTimeValue + "> ?existingValueNode . \n" +
+ protected String getExistingPrecisionQuery() {
+ return "SELECT ?existingPrecision WHERE { \n" +
+ "?subject <" + this.getToDateTimeValuePredicate() + "> ?existingValueNode . \n" +
"?existingValueNode a <" + valueType + "> . \n" +
"?existingValueNode <" + dateTimePrecision + "> ?existingPrecision }";
-
- final static String existingNodeQuery =
- "SELECT ?existingNode WHERE { \n" +
- "?subject <" + toDateTimeValue + "> ?existingNode . \n" +
+ }
+ protected String getExistingNodeQuery() {
+ return "SELECT ?existingNode WHERE { \n" +
+ "?subject <" + this.getToDateTimeValuePredicate() + "> ?existingNode . \n" +
"?existingNode a <" + valueType + "> }";
+ }
+ public static String getNodeVar() {
+ return "valueNode";
+ }
+
+ public static String getNodeN3Var() {
+ return "?" + getNodeVar();
+ }
+ //isolating the predicate in this fashion allows this class to be subclassed for other date time value
+ //properties
+ protected String getToDateTimeValuePredicate() {
+ return this.toDateTimeValue;
+ }
+
+ protected String getDateTimeFieldName() {
+ return "dateTimeField";
+ }
+
+ protected String getTemplate() {
+ return "dateTimeValueForm.ftl";
+ }
//Adding form specific data such as edit mode
public void addFormSpecificData(EditConfigurationVTwo editConfiguration, VitroRequest vreq) {
HashMap formSpecificData = new HashMap();
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
index 378e4069e..a6ea9e704 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
@@ -7,6 +7,7 @@ import static edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -101,15 +102,23 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene
}
protected List getRangeTypes(VitroRequest vreq) {
- Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq);
- String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
+ Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq);
+ String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
WebappDaoFactory wDaoFact = vreq.getWebappDaoFactory();
List types = new ArrayList();
- List vclasses = new ArrayList();
- vclasses = wDaoFact.getVClassDao().getVClassesForProperty(subject.getVClassURI(),predicateUri);
- for(VClass v: vclasses) {
- types.add(v.getURI());
- }
+ //Get all vclasses applicable to subject
+ List vClasses = subject.getVClasses();
+ HashSet typesHash = new HashSet();
+ for(VClass vclass: vClasses) {
+ List rangeVclasses = wDaoFact.getVClassDao().getVClassesForProperty(vclass.getURI(),predicateUri);
+ if(rangeVclasses != null) {
+ for(VClass range: rangeVclasses) {
+ //a hash will keep a unique list of types and so prevent duplicates
+ typesHash.add(range.getURI());
+ }
+ }
+ }
+ types.addAll(typesHash);
return types;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/EditConfigurationAJAXGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/EditConfigurationAJAXGenerator.java
new file mode 100644
index 000000000..c65548dad
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/EditConfigurationAJAXGenerator.java
@@ -0,0 +1,11 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators;
+
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
+
+//For use with classes that explicitly modify configurations through AJAX requests
+public interface EditConfigurationAJAXGenerator {
+ public void modifyEditConfiguration( EditConfigurationVTwo config, VitroRequest vreq ) throws Exception;
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java
index 8fb98af21..a3409cd3d 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java
@@ -128,10 +128,11 @@ public class ProcessSparqlDataGetterN3 extends ProcessDataGetterAbstract {
existingLiteralValues.put(this.getVarName("query", counter),
new ArrayList(Arrays.asList(queryLiteral)));
-
- existingUriValues.put(this.getVarName("queryModel", counter),
+ //Query model is optional
+ if(queryModelResource != null && queryModelResource.getURI() != null) {
+ existingUriValues.put(this.getVarName("queryModel", counter),
new ArrayList(Arrays.asList(queryModelResource.getURI())));
-
+ }
}
} catch(Exception ex) {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestAJAXController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestAJAXController.java
new file mode 100644
index 000000000..e09aa6ae4
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestAJAXController.java
@@ -0,0 +1,132 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.edit.n3editing.controller;
+
+import static edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils.getPredicateUri;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.hp.hpl.jena.vocabulary.RDFS;
+
+import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
+import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
+import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
+import edu.cornell.mannlib.vitro.webapp.beans.Individual;
+import edu.cornell.mannlib.vitro.webapp.beans.Property;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.DirectRedirectResponseValues;
+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.controller.json.GetDataForPage;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetEntitiesByVClass;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetEntitiesByVClassContinuation;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetRenderedSolrIndividualsByVClass;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetSolrIndividualsByVClass;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetSolrIndividualsByVClasses;
+import edu.cornell.mannlib.vitro.webapp.controller.json.GetVClassesForVClassGroup;
+import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
+import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditSubmissionUtils;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.MultiValueEditSubmission;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.EditConfigurationAJAXGenerator;
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.EditConfigurationGenerator;
+import edu.cornell.mannlib.vitro.webapp.utils.log.LogUtils;
+import edu.cornell.mannlib.vitro.webapp.web.templatemodels.edit.EditConfigurationTemplateModel;
+import edu.cornell.mannlib.vitro.webapp.web.templatemodels.edit.MultiValueEditSubmissionTemplateModel;
+
+/**
+ * This servlet is intended to handle all requests to create a form for use
+ * by the N3 editing system. It will examine the request parameters, determine
+ * which form to use, execute a EditConfiguration setup, and evaluate the
+ * view indicated by the EditConfiguration.
+ *
+ * Do not add code to this class to achieve some behavior in a
+ * form. Try adding the behavior logic to the code that generates the
+ * EditConfiguration for the form.
+ */
+public class EditRequestAJAXController extends VitroHttpServlet {
+ private static final long serialVersionUID = 1L;
+ public static Log log = LogFactory.getLog(EditRequestDispatchController.class);
+
+
+ protected Actions requiredActions(VitroRequest vreq) {
+ return SimplePermission.DO_FRONT_END_EDITING.ACTIONS;
+ }
+
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ super.doPost(req, resp);
+ }
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ super.doGet(req, resp);
+ log.debug(LogUtils.formatRequestProperties(log, "debug", req));
+
+ VitroRequest vreq = new VitroRequest(req);
+ try {
+ //Get edit configuration object based on editk key in request
+ EditConfigurationVTwo config = getEditConfiguration(vreq);
+ //Get the generator name also from the request parameter
+ String generatorName = vreq.getParameter("generator");
+ String javaGeneratorName = "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators." + generatorName;
+ //TODO: Add this method to generators, but for now constructing a new class of generator specifically for AJAX requests
+ EditConfigurationAJAXGenerator generator = getAJAXGenerator(javaGeneratorName, vreq, vreq.getSession());
+ //run the modification
+ generator.modifyEditConfiguration(config, vreq);
+ } catch(Exception ex) {
+ log.error("An error occurred in retrieving configuration and/or generator ", ex);
+ }
+
+ }
+
+ protected EditConfigurationVTwo getEditConfiguration(VitroRequest vreq) {
+
+ EditConfigurationVTwo config = EditConfigurationVTwo.getConfigFromSession(vreq.getSession(), vreq);
+ return config;
+ }
+
+ private EditConfigurationAJAXGenerator getAJAXGenerator(
+ String editConfGeneratorName, VitroRequest vreq, HttpSession session) throws Exception {
+
+ EditConfigurationAJAXGenerator EditConfigurationVTwoGenerator = null;
+
+ Object object = null;
+ try {
+ Class classDefinition = Class.forName(editConfGeneratorName);
+ object = classDefinition.newInstance();
+ EditConfigurationVTwoGenerator = (EditConfigurationAJAXGenerator) object;
+ } catch (InstantiationException e) {
+ System.out.println(e);
+ } catch (IllegalAccessException e) {
+ System.out.println(e);
+ } catch (ClassNotFoundException e) {
+ System.out.println(e);
+ }
+
+ if(EditConfigurationVTwoGenerator == null){
+ throw new Error("Could not find EditConfigurationVTwoGenerator " + editConfGeneratorName);
+ } else {
+ return EditConfigurationVTwoGenerator;
+ }
+
+ }
+
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java
index 07308865b..a122eaaeb 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/EditRequestDispatchController.java
@@ -155,12 +155,25 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet {
private EditConfigurationVTwo setupEditConfiguration(String editConfGeneratorName,
VitroRequest vreq) throws Exception {
HttpSession session = vreq.getSession();
- EditConfigurationVTwo editConfig =
- makeEditConfigurationVTwo( editConfGeneratorName, vreq, session);
-
- //edit key is set here, NOT in the generator class
- String editKey = EditConfigurationUtils.getEditKey(vreq);
- editConfig.setEditKey(editKey);
+ //Originally, this code called makeEditConfiguration before checking for/setting the edit key
+ //which meant that in the case of page reload on an error, you would recreate an edit configuration
+ //using the generator
+ //Given recent updates enabling modification of edit configuration dynamically through AJAX,
+ //we will first check whether the edit key exists and if there is already an edit configuration
+ //in the session - and then will utilize the edit configuration that already exists
+ //edit key is set here, NOT in the generator class
+ EditConfigurationVTwo editConfig = null;
+ EditConfigurationVTwo existingConfig = EditConfigurationVTwo.getConfigFromSession(session, vreq);
+ if(existingConfig != null) {
+ editConfig = existingConfig;
+ } else {
+ editConfig =
+ makeEditConfigurationVTwo( editConfGeneratorName, vreq, session);
+ }
+
+ String editKey = EditConfigurationUtils.getEditKey(vreq);
+ editConfig.setEditKey(editKey);
+
//put edit configuration in session so it can be accessed on form submit.
EditConfigurationVTwo.putConfigInSession(editConfig, session);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java
index 802508a8c..3f7aa29e0 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java
@@ -35,7 +35,7 @@ public class FileStorageSetup implements ServletContextListener {
* {@link ConfigurationProperties} for the vivo home directory. The file
* storage base directory is in a subdirectory below this one.
*/
- public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home.directory";
+ public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home";
public static final String FILE_STORAGE_SUBDIRECTORY = "uploads";
/**
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/serving/FileServingServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/serving/FileServingServlet.java
index 53c30e304..388b71e38 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/serving/FileServingServlet.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/serving/FileServingServlet.java
@@ -94,6 +94,10 @@ public class FileServingServlet extends VitroHttpServlet {
String actualFilename = findAndValidateFilename(fileInfo, path);
in = openImageInputStream(fileInfo, actualFilename);
+ } catch (FileServingException e) {
+ log.info("Failed to serve the file at '" + path + "' -- " + e.getMessage());
+ in = openMissingLinkImage(request);
+ mimeType = "image/png";
} catch (Exception e) {
log.warn("Failed to serve the file at '" + path + "' -- " + e.getMessage());
in = openMissingLinkImage(request);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java
index 4a145dc8d..3ef123eb4 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java
@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
import freemarker.cache.WebappTemplateLoader;
import freemarker.template.Configuration;
@@ -67,36 +69,36 @@ public class StartupStatusDisplayFilter implements Filter {
statusAlreadyDisplayed = true;
}
- private void displayStartupStatus(ServletRequest req, ServletResponse resp) throws IOException,
- ServletException {
- HttpServletResponse hResp = (HttpServletResponse) resp;
+ private void displayStartupStatus(ServletRequest req, ServletResponse resp)
+ throws IOException, ServletException {
+ HttpServletResponse hresp = (HttpServletResponse) resp;
+ HttpServletRequest hreq = (HttpServletRequest) req;
try {
Map bodyMap = new HashMap();
bodyMap.put("status", ss);
bodyMap.put("showLink", !isFatal());
bodyMap.put("contextPath", getContextPath());
- bodyMap.put("applicationName", getApplicationName());
-
- HttpServletRequest httpreq = (HttpServletRequest) req;
- String url = "";
-
- String path = httpreq.getRequestURI();
- if( path != null ){
- url = path;
- }
-
- String query = httpreq.getQueryString();
- if( !StringUtils.isEmpty( query )){
- url = url + "?" + query;
- }
-
- bodyMap.put("url", url );
+ bodyMap.put("applicationName", getApplicationName());
- hResp.setContentType("text/html;charset=UTF-8");
- hResp.setStatus(SC_INTERNAL_SERVER_ERROR);
+ String url = "";
+
+ String path = hreq.getRequestURI();
+ if (path != null) {
+ url = path;
+ }
+
+ String query = hreq.getQueryString();
+ if (!StringUtils.isEmpty(query)) {
+ url = url + "?" + query;
+ }
+
+ bodyMap.put("url", url);
+
+ hresp.setContentType("text/html;charset=UTF-8");
+ hresp.setStatus(SC_INTERNAL_SERVER_ERROR);
Template tpl = loadFreemarkerTemplate();
- tpl.process(bodyMap, hResp.getWriter());
+ tpl.process(bodyMap, hresp.getWriter());
} catch (TemplateException e) {
throw new ServletException("Problem with Freemarker Template", e);
}
@@ -114,7 +116,9 @@ public class StartupStatusDisplayFilter implements Filter {
private Object getApplicationName() {
String name = "";
try {
- ApplicationBean app = ApplicationBean.getAppBean(ctx);
+ WebappDaoFactory wadf = (WebappDaoFactory) ctx
+ .getAttribute("webappDaoFactory");
+ ApplicationBean app = wadf.getApplicationDao().getApplicationBean();
name = app.getApplicationName();
} catch (Exception e) {
// deal with problems below
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/VitroRequestPrep.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/VitroRequestPrep.java
index 02924963f..f1d7a6e71 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/VitroRequestPrep.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/VitroRequestPrep.java
@@ -38,7 +38,6 @@ import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
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.policy.ServletPolicyList;
-import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
@@ -82,19 +81,10 @@ public class VitroRequestPrep implements Filter {
};
private ServletContext _context;
- private ApplicationBean _appbean;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
_context = filterConfig.getServletContext();
-
- Object o = _context.getAttribute("applicationBean");
- if (o instanceof ApplicationBean) {
- _appbean = (ApplicationBean) o;
- } else {
- _appbean = new ApplicationBean();
- }
- log.debug("VitroRequestPrep: AppBean theme " + _appbean.getThemeDir());
}
@Override
@@ -132,9 +122,6 @@ public class VitroRequestPrep implements Filter {
VitroRequest vreq = new VitroRequest(req);
- //-- setup appBean --//
- vreq.setAppBean(_appbean);
-
//-- setup DAO factory --//
WebappDaoFactory wdf = getWebappDaoFactory(vreq);
//TODO: get accept-language from request and set as preferred languages
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java
new file mode 100644
index 000000000..3bc2d6045
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java
@@ -0,0 +1,231 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.ResourceBundle.Control;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+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.controller.VitroRequest;
+
+/**
+ * Provides access to a bundle of text strings, based on the name of the bundle,
+ * the Locale of the requesting browser, and the current theme directory.
+ *
+ * If the bundle name is not specified, the default name of "all" is used.
+ *
+ * If a requested bundle is not found, no error is thrown. Instead, an empty
+ * bundle is returned that produces error message strings when asked for text.
+ */
+public class I18n {
+ private static final Log log = LogFactory.getLog(I18n.class);
+
+ public static final String DEFAULT_BUNDLE_NAME = "all";
+ private static final String PROPERTY_DEVELOPER_DEFEAT_CACHE = "developer.defeatI18nCache";
+
+ /**
+ * This is where the work gets done. Not declared final, so it can be
+ * modified in unit tests.
+ */
+ private static I18n instance = new I18n();
+
+ // ----------------------------------------------------------------------
+ // Static methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * A convenience method to get a bundle and format the text.
+ */
+ public static String text(String bundleName, HttpServletRequest req,
+ String key, Object... parameters) {
+ return bundle(bundleName, req).text(key, parameters);
+ }
+
+ /**
+ * A convenience method to get the default bundle and format the text.
+ */
+ public static String text(HttpServletRequest req, String key,
+ Object... parameters) {
+ return bundle(req).text(key, parameters);
+ }
+
+ /**
+ * Get a I18nBundle by this name.
+ */
+ public static I18nBundle bundle(String bundleName, HttpServletRequest req) {
+ return instance.getBundle(bundleName, req);
+ }
+
+ /**
+ * Get the default I18nBundle.
+ */
+ public static I18nBundle bundle(HttpServletRequest req) {
+ return instance.getBundle(DEFAULT_BUNDLE_NAME, req);
+ }
+
+ // ----------------------------------------------------------------------
+ // The instance
+ // ----------------------------------------------------------------------
+
+ /** Holds the current theme directory, as far as we know. */
+ private AtomicReference themeDirectory = new AtomicReference(
+ "");
+
+ /**
+ * Get an I18nBundle by this name. The request provides the preferred
+ * Locale, the application directory, the theme directory and the
+ * development mode flag.
+ *
+ * If the request indicates that the system is in development mode, then the
+ * cache is cleared on each request.
+ *
+ * If the theme directory has changed, the cache is cleared.
+ */
+ private I18nBundle getBundle(String bundleName, HttpServletRequest req) {
+ log.debug("Getting bundle '" + bundleName + "'");
+
+ try {
+ checkDevelopmentMode(req);
+ checkForChangeInThemeDirectory(req);
+
+ String dir = themeDirectory.get();
+ ServletContext ctx = req.getSession().getServletContext();
+
+ ResourceBundle.Control control = getControl(ctx, dir);
+ ResourceBundle rb = ResourceBundle.getBundle(bundleName,
+ req.getLocale(), control);
+ return new I18nBundle(bundleName, rb);
+ } catch (MissingResourceException e) {
+ log.warn("Didn't find text bundle '" + bundleName + "'");
+ return I18nBundle.emptyBundle(bundleName);
+ } catch (Exception e) {
+ log.error("Failed to create text bundle '" + bundleName + "'", e);
+ return I18nBundle.emptyBundle(bundleName);
+ }
+ }
+
+ /**
+ * If we are in development mode, clear the cache on each request.
+ */
+ private void checkDevelopmentMode(HttpServletRequest req) {
+ ConfigurationProperties bean = ConfigurationProperties.getBean(req);
+
+ String flag = bean
+ .getProperty(PROPERTY_DEVELOPER_DEFEAT_CACHE, "false");
+ if (Boolean.valueOf(flag.trim())) {
+ log.debug("In development mode - clearing the cache.");
+ ResourceBundle.clearCache();
+ }
+ }
+
+ /**
+ * If the theme directory has changed from before, clear the cache of all
+ * ResourceBundles.
+ */
+ private void checkForChangeInThemeDirectory(HttpServletRequest req) {
+ String currentDir = new VitroRequest(req).getAppBean().getThemeDir();
+ String previousDir = themeDirectory.getAndSet(currentDir);
+ if (!currentDir.equals(previousDir)) {
+ log.debug("Theme directory changed from '" + previousDir + "' to '"
+ + currentDir + "' - clearing the cache.");
+ ResourceBundle.clearCache();
+ }
+ }
+
+ /**
+ * Override this method in the unit tests, to return a more testable Control
+ * instance.
+ */
+ protected Control getControl(ServletContext ctx, String dir) {
+ return new ThemeBasedControl(ctx, dir);
+ }
+
+ // ----------------------------------------------------------------------
+ // Control classes for instantiating ResourceBundles
+ // ----------------------------------------------------------------------
+
+ /**
+ * Instead of looking in the classpath, look in the theme i18n directory and
+ * the application i18n directory.
+ */
+ private static class ThemeBasedControl extends ResourceBundle.Control {
+ private static final String BUNDLE_DIRECTORY = "i18n/";
+ private final ServletContext ctx;
+ private final String themeDirectory;
+
+ public ThemeBasedControl(ServletContext ctx, String themeDirectory) {
+ this.ctx = ctx;
+ this.themeDirectory = themeDirectory;
+ }
+
+ /**
+ * Don't look for classes to satisfy the request, just property files.
+ */
+ @Override
+ public List getFormats(String baseName) {
+ return FORMAT_PROPERTIES;
+ }
+
+ /**
+ * Don't look in the class path, look in the current servlet context, in
+ * the bundle directory under the theme directory and in the bundle
+ * directory under the application directory.
+ */
+ @Override
+ public ResourceBundle newBundle(String baseName, Locale locale,
+ String format, ClassLoader loader, boolean reload)
+ throws IllegalAccessException, InstantiationException,
+ IOException {
+ checkArguments(baseName, locale, format);
+
+ log.debug("Creating bundle for '" + baseName + "', " + locale
+ + ", '" + format + "', " + reload);
+
+ String bundleName = toBundleName(baseName, locale);
+ if (bundleName == null) {
+ throw new NullPointerException("bundleName may not be null.");
+ }
+
+ String themeI18nPath = "/" + themeDirectory + BUNDLE_DIRECTORY;
+ String appI18nPath = "/" + BUNDLE_DIRECTORY;
+
+ log.debug("Paths are '" + themeI18nPath + "' and '" + appI18nPath
+ + "'");
+
+ return VitroResourceBundle.getBundle(bundleName, ctx, appI18nPath,
+ themeI18nPath, this);
+ }
+
+ /**
+ * The documentation for ResourceBundle.Control.newBundle() says I
+ * should throw these exceptions.
+ */
+ private void checkArguments(String baseName, Locale locale,
+ String format) {
+ if (baseName == null) {
+ throw new NullPointerException("baseName may not be null.");
+ }
+ if (locale == null) {
+ throw new NullPointerException("locale may not be null.");
+ }
+ if (format == null) {
+ throw new NullPointerException("format may not be null.");
+ }
+ if (!FORMAT_DEFAULT.contains(format)) {
+ throw new IllegalArgumentException(
+ "format must be one of these: " + FORMAT_DEFAULT);
+ }
+ }
+ }
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java
new file mode 100644
index 000000000..7ea5acc3d
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18nBundle.java
@@ -0,0 +1,102 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n;
+
+import java.text.MessageFormat;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A wrapper for a ResourceBundle that will not throw an exception, no matter
+ * what string you request.
+ *
+ * If the ResourceBundle was not found, or if it doesn't contain the requested
+ * key, an error message string is returned, to help the developer diagnose the
+ * problem.
+ */
+public class I18nBundle {
+ private static final Log log = LogFactory.getLog(I18nBundle.class);
+
+ private static final String MESSAGE_BUNDLE_NOT_FOUND = "Text bundle ''{0}'' not found.";
+ private static final String MESSAGE_KEY_NOT_FOUND = "Text bundle ''{0}'' has no text for ''{1}''";
+
+ public static I18nBundle emptyBundle(String bundleName) {
+ return new I18nBundle(bundleName);
+ }
+
+ private final String bundleName;
+ private final ResourceBundle resources;
+ private final String notFoundMessage;
+
+ private I18nBundle(String bundleName) {
+ this(bundleName, new EmptyResourceBundle(), MESSAGE_BUNDLE_NOT_FOUND);
+ }
+
+ public I18nBundle(String bundleName, ResourceBundle resources) {
+ this(bundleName, resources, MESSAGE_KEY_NOT_FOUND);
+ }
+
+ private I18nBundle(String bundleName, ResourceBundle resources, String notFoundMessage) {
+ if (bundleName == null) {
+ throw new IllegalArgumentException("bundleName may not be null");
+ }
+ if (bundleName.isEmpty()) {
+ throw new IllegalArgumentException("bundleName may not be empty");
+ }
+ if (resources == null) {
+ throw new NullPointerException("resources may not be null.");
+ }if (notFoundMessage == null) {
+ throw new NullPointerException("notFoundMessage may not be null.");
+ }
+ this.bundleName = bundleName;
+ this.resources = resources;
+ this.notFoundMessage = notFoundMessage;
+ }
+
+ public String text(String key, Object... parameters) {
+ log.debug("Asking for '" + key + "' from bundle '" + bundleName + "'");
+
+ String textString;
+ if (resources.containsKey(key)) {
+ textString = resources.getString(key);
+ return formatString(textString, parameters);
+ } else {
+ String message = MessageFormat.format(notFoundMessage, bundleName,
+ key);
+ log.warn(message);
+ return "ERROR: " + message;
+ }
+ }
+
+ private static String formatString(String textString, Object... parameters) {
+ if (parameters.length == 0) {
+ return textString;
+ } else {
+ return MessageFormat.format(textString, parameters);
+ }
+ }
+
+ /**
+ * A resource bundle that contains no strings.
+ */
+ public static class EmptyResourceBundle extends ResourceBundle {
+ @Override
+ public Enumeration getKeys() {
+ return Collections.enumeration(Collections. emptySet());
+ }
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key == null) {
+ throw new NullPointerException("key may not be null.");
+ }
+ return null;
+ }
+
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java
new file mode 100644
index 000000000..c9b2b2acb
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java
@@ -0,0 +1,211 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.ResourceBundle;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Works like a PropertyResourceBundle with two exceptions:
+ *
+ * It looks for the file in both the i18n directory of the theme and in the i18n
+ * directory of the application. Properties found in the theme override those
+ * found in the application.
+ *
+ * It allows a property to take its contents from a file. File paths are
+ * relative to the i18n directory. Again, a file in the theme will override one
+ * in the application.
+ *
+ * If a property has a value (after overriding) of "@@file ", the
+ * bundle looks for the file relative to the i18n directory of the theme, then
+ * relative to the i18n directory of the application. If the file is not found
+ * in either location, a warning is written to the log and the property will
+ * contain an error message for displayed.
+ *
+ * Note that the filename is not manipulated for Locale, so the author of the
+ * properties files must do it explicitly. For example:
+ *
+ * In all.properties: account_email_html = @@file accountEmail.html
+ *
+ * In all_es.properties: account_email_html = @@file accountEmail_es.html
+ */
+public class VitroResourceBundle extends ResourceBundle {
+ private static final Log log = LogFactory.getLog(VitroResourceBundle.class);
+
+ private static final String FILE_FLAG = "@@file ";
+ private static final String MESSAGE_FILE_NOT_FOUND = "File {1} not found for property {0}.";
+
+ // ----------------------------------------------------------------------
+ // Factory method
+ // ----------------------------------------------------------------------
+
+ public static VitroResourceBundle getBundle(String bundleName,
+ ServletContext ctx, String appI18nPath, String themeI18nPath,
+ Control control) {
+ try {
+ return new VitroResourceBundle(bundleName, ctx, appI18nPath,
+ themeI18nPath, control);
+ } catch (FileNotFoundException e) {
+ log.debug(e);
+ return null;
+ } catch (Exception e) {
+ log.warn(e, e);
+ return null;
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // The instance
+ // ----------------------------------------------------------------------
+
+ private final String bundleName;
+ private final ServletContext ctx;
+ private final String appI18nPath;
+ private final String themeI18nPath;
+ private final Control control;
+ private final Properties defaults;
+ private final Properties properties;
+
+ private VitroResourceBundle(String bundleName, ServletContext ctx,
+ String appI18nPath, String themeI18nPath, Control control)
+ throws IOException {
+ this.bundleName = bundleName;
+ this.ctx = ctx;
+ this.appI18nPath = appI18nPath;
+ this.themeI18nPath = themeI18nPath;
+ this.control = control;
+
+ this.defaults = new Properties();
+ this.properties = new Properties(this.defaults);
+
+ loadProperties();
+ loadReferencedFiles();
+ }
+
+ private void loadProperties() throws IOException {
+ String resourceName = control.toResourceName(bundleName, "properties");
+
+ String defaultsPath = joinPath(appI18nPath, resourceName);
+ String propertiesPath = joinPath(themeI18nPath, resourceName);
+ File defaultsFile = locateFile(defaultsPath);
+ File propertiesFile = locateFile(propertiesPath);
+
+ if ((defaultsFile == null) && (propertiesFile == null)) {
+ throw new FileNotFoundException("Property file not found at '"
+ + defaultsPath + "' or '" + propertiesPath + "'");
+ }
+
+ if (defaultsFile != null) {
+ log.debug("Loading bundle '" + bundleName + "' defaults from '"
+ + defaultsPath + "'");
+ FileInputStream stream = new FileInputStream(defaultsFile);
+ Reader reader = new InputStreamReader(stream, "UTF-8");
+ try {
+ this.defaults.load(reader);
+ } finally {
+ reader.close();
+ }
+ }
+ if (propertiesFile != null) {
+ log.debug("Loading bundle '" + bundleName + "' overrides from '"
+ + propertiesPath + "'");
+ FileInputStream stream = new FileInputStream(propertiesFile);
+ Reader reader = new InputStreamReader(stream, "UTF-8");
+ try {
+ this.properties.load(reader);
+ } finally {
+ reader.close();
+ }
+ }
+ }
+
+ private void loadReferencedFiles() throws IOException {
+ for (String key : this.properties.stringPropertyNames()) {
+ String value = this.properties.getProperty(key);
+ if (value.startsWith(FILE_FLAG)) {
+ String filepath = value.substring(FILE_FLAG.length()).trim();
+ loadReferencedFile(key, filepath);
+ }
+ }
+ }
+
+ private void loadReferencedFile(String key, String filepath)
+ throws IOException {
+ String appFilePath = joinPath(appI18nPath, filepath);
+ String themeFilePath = joinPath(themeI18nPath, filepath);
+ File appFile = locateFile(appFilePath);
+ File themeFile = locateFile(themeFilePath);
+
+ if (themeFile != null) {
+ this.properties.setProperty(key,
+ FileUtils.readFileToString(themeFile, "UTF-8"));
+ } else if (appFile != null) {
+ this.properties.setProperty(key,
+ FileUtils.readFileToString(appFile, "UTF-8"));
+ } else {
+ String message = MessageFormat.format(MESSAGE_FILE_NOT_FOUND, key,
+ themeFilePath, appFilePath);
+ this.properties.setProperty(key, message);
+ log.warn(message);
+ }
+ }
+
+ private String joinPath(String root, String twig) {
+ if ((root.charAt(root.length() - 1) == File.separatorChar)
+ || (twig.charAt(0) == File.separatorChar)) {
+ return root + twig;
+ } else {
+ return root + File.separatorChar + twig;
+ }
+ }
+
+ private File locateFile(String path) {
+ String realPath = ctx.getRealPath(path);
+ if (realPath == null) {
+ log.debug("No real path for '" + path + "'");
+ return null;
+ }
+
+ File f = new File(realPath);
+ if (!f.isFile()) {
+ log.debug("No file at '" + realPath + "'");
+ return null;
+ }
+ if (!f.canRead()) {
+ log.error("Can't read the file at '" + realPath + "'");
+ return null;
+ }
+ log.debug("Located file '" + path + "' at '" + realPath + "'");
+ return f;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Enumeration getKeys() {
+ return (Enumeration) this.properties.propertyNames();
+ }
+
+ @Override
+ protected Object handleGetObject(String key) {
+ String value = this.properties.getProperty(key);
+ if (value == null) {
+ log.debug(bundleName + " has no value for '" + key + "'");
+ }
+ return value;
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nBundleTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nBundleTemplateModel.java
new file mode 100644
index 000000000..def580202
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nBundleTemplateModel.java
@@ -0,0 +1,40 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n.freemarker;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * For Freemarker, this acts like a bundle of text strings. It is simply a
+ * wrapper around an I18nBundle.
+ */
+public class I18nBundleTemplateModel implements TemplateHashModel {
+ private static final Log log = LogFactory
+ .getLog(I18nBundleTemplateModel.class);
+
+ private final String bundleName;
+ private final I18nBundle textBundle;
+
+ public I18nBundleTemplateModel(String bundleName, I18nBundle textBundle) {
+ this.bundleName = bundleName;
+ this.textBundle = textBundle;
+ }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ return new I18nStringTemplateModel(bundleName, key,
+ textBundle.text(key));
+ }
+
+ @Override
+ public boolean isEmpty() throws TemplateModelException {
+ return false;
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nMethodModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nMethodModel.java
new file mode 100644
index 000000000..4e56896f4
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nMethodModel.java
@@ -0,0 +1,50 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n.freemarker;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
+import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle;
+import freemarker.core.Environment;
+import freemarker.template.TemplateMethodModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * This Freemarker method will produce a bundle of text strings. It is simply a
+ * wrapper around I18n that produces a wrapped I18nBundle.
+ *
+ * If the bundle name is not provided, the default bundle is assumed.
+ */
+public class I18nMethodModel implements TemplateMethodModel {
+ private static final Log log = LogFactory.getLog(I18nMethodModel.class);
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object exec(List args) throws TemplateModelException {
+ if (args.size() > 1) {
+ throw new TemplateModelException("Too many arguments: "
+ + "displayText method only requires a bundle name.");
+ }
+ Object arg = args.isEmpty() ? I18n.DEFAULT_BUNDLE_NAME : args.get(0);
+ if (!(arg instanceof String)) {
+ throw new IllegalArgumentException(
+ "Arguments to a TemplateMethodModel are supposed to be Strings!");
+ }
+
+ log.debug("Asking for this bundle: " + arg);
+ String bundleName = (String) arg;
+
+ Environment env = Environment.getCurrentEnvironment();
+ HttpServletRequest request = (HttpServletRequest) env
+ .getCustomAttribute("request");
+ I18nBundle tb = I18n.bundle(bundleName, request);
+ return new I18nBundleTemplateModel(bundleName, tb);
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nStringTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nStringTemplateModel.java
new file mode 100644
index 000000000..e11ae6640
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/freemarker/I18nStringTemplateModel.java
@@ -0,0 +1,79 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n.freemarker;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import freemarker.template.TemplateMethodModelEx;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateScalarModel;
+import freemarker.template.utility.DeepUnwrap;
+
+/**
+ * A Freemarker representation of a text string. Because it implements
+ * TemplateScalarModel, you can use it as a string value. And because it
+ * implements TemplateMethodModel, you can pass arguments to it for formatting.
+ *
+ * So if the string is "His name is {0}!", then these references could be used:
+ *
+ * ${string} ==> "His name is {0}!"
+ *
+ * ${string("Bozo")} ==> "His name is Bozo!"
+ *
+ * Note that the format of the message is determined by java.text.MessageFormat,
+ * so argument indices start at 0 and you can escape a substring by wrapping it
+ * in apostrophes.
+ */
+public class I18nStringTemplateModel implements TemplateMethodModelEx,
+ TemplateScalarModel {
+ private static final Log log = LogFactory
+ .getLog(I18nStringTemplateModel.class);
+
+ private final String bundleName;
+ private final String key;
+ private final String textString;
+
+ public I18nStringTemplateModel(String bundleName, String key,
+ String textString) {
+ this.bundleName = bundleName;
+ this.key = key;
+ this.textString = textString;
+ }
+
+ @Override
+ public String getAsString() throws TemplateModelException {
+ return textString;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public Object exec(List args) throws TemplateModelException {
+ log.debug("Formatting string '" + key + "' from bundle '" + bundleName
+ + "' with these arguments: " + args);
+
+ if (args.isEmpty()) {
+ return textString;
+ } else {
+ Object[] unwrappedArgs = new Object[args.size()];
+ for (int i = 0; i < args.size(); i++) {
+ unwrappedArgs[i] = DeepUnwrap.unwrap((TemplateModel) args
+ .get(i));
+ }
+ try {
+ return MessageFormat.format(textString, unwrappedArgs);
+ } catch (Exception e) {
+ String message = "Can't format '" + key + "' from bundle '"
+ + bundleName + "', wrong argument types: " + args
+ + " for message format'" + textString + "'";
+ log.warn(message);
+ return message;
+ }
+ }
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java
new file mode 100644
index 000000000..523a263a2
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java
@@ -0,0 +1,100 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n.selection;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.LocaleUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
+
+/**
+ * Call this at /selectLocale&selection=[locale_string]
+ *
+ * For example: /selectLocale&selection=en_US or /selectLocale&selection=es
+ *
+ * Write an error to the log (and to DisplayMessage) if the selection is not
+ * syntactically valid.
+ *
+ * Write a warning to the log if the selection code is not one of the selectable
+ * Locales from runtime.properties, or if the selection code is not recognized
+ * by the system.
+ *
+ * Set the new Locale in the Session using SelectedLocale and return to the
+ * referrer.
+ */
+public class LocaleSelectionController extends HttpServlet {
+ private static final Log log = LogFactory
+ .getLog(LocaleSelectionController.class);
+
+ public static final String PARAMETER_SELECTION = "selection";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ String referrer = req.getHeader("referer");
+
+ String selectedLocale = req.getParameter(PARAMETER_SELECTION);
+
+ try {
+ processSelectedLocale(req, selectedLocale);
+ } catch (Exception e) {
+ log.error("Failed to process the user's Locale selection", e);
+ }
+
+ if (StringUtils.isEmpty(referrer)) {
+ resp.sendRedirect(UrlBuilder.getHomeUrl());
+ } else {
+ resp.sendRedirect(referrer);
+ }
+ }
+
+ private void processSelectedLocale(HttpServletRequest req,
+ String selectedLocale) {
+ if (StringUtils.isBlank(selectedLocale)) {
+ log.debug("No '" + PARAMETER_SELECTION + "' parameter");
+ return;
+ }
+
+ Locale locale = null;
+
+ try {
+ locale = LocaleUtils.toLocale(selectedLocale.trim());
+ log.debug("Locale selection is " + locale);
+ } catch (IllegalArgumentException e) {
+ log.error("Failed to convert the selection to a Locale", e);
+ DisplayMessage.setMessage(req,
+ "There was a problem in the system. "
+ + "Your language choice was rejected.");
+ return;
+ }
+
+ List selectables = SelectedLocale.getSelectableLocales(req);
+ if (!selectables.contains(locale)) {
+ log.warn("User selected a locale '" + locale
+ + "' that was not in the list: " + selectables);
+ } else if (!LocaleUtils.isAvailableLocale(locale)) {
+ log.warn("User selected an unrecognized locale: '" + locale + "'");
+ }
+
+ SelectedLocale.setSelectedLocale(req, locale);
+ log.debug("Setting selected locale to " + locale);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ doGet(req, resp);
+ }
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java
new file mode 100644
index 000000000..627ad04ca
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java
@@ -0,0 +1,95 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.i18n.selection;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+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.controller.freemarker.UrlBuilder;
+import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
+
+/**
+ * Get the data for the selectable Locales, so the Freemarker template can
+ * create a row of flag images that will select the desired locale.
+ *
+ * If there are no selectable Locales in runtime.properties, we return an empty
+ * map. (selectLocale?? will return false)
+ *
+ * If the Locale has been forced by runtime.properties, we do the same.
+ *
+ * If there are selectable Locales, the returned map will contain a structure
+ * like this:
+ *
+ *
+ * {selectLocale={
+ * selectLocaleUrl = [the URL for the form action to select a Locale]
+ * locales={ [a list of maps]
+ * { [a map for each Locale]
+ * code = [the code for the Locale, e.g. "en_US"]
+ * label = [the alt text for the Locale, e.g. "Spanish (Spain)"]
+ * imageUrl = [the URL of the image that represents the Locale]
+ * }
+ * }
+ * }
+ * }
+ *
+ */
+public class LocaleSelectionDataGetter implements DataGetter {
+ private static final Log log = LogFactory
+ .getLog(LocaleSelectionDataGetter.class);
+
+ private final VitroRequest vreq;
+
+ public LocaleSelectionDataGetter(VitroRequest vreq) {
+ this.vreq = vreq;
+ }
+
+ @Override
+ public Map getData(Map valueMap) {
+ List selectables = SelectedLocale.getSelectableLocales(vreq);
+ if (selectables.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Map result = new HashMap();
+ result.put("selectLocaleUrl", UrlBuilder.getUrl("/selectLocale"));
+ result.put("locales", buildLocalesList(selectables));
+
+ Map bodyMap = new HashMap();
+ bodyMap.put("selectLocale", result);
+ log.debug("Sending these values: " + bodyMap);
+ return bodyMap;
+ }
+
+ private List