diff --git a/build.gradle b/build.gradle
index 1566ec681f6c90dd578eed4de85aa23e2eb6e8df..6ae3b8079085d340b22c7c8a7e16d94a15a7d846 100644
--- a/build.gradle
+++ b/build.gradle
@@ -93,8 +93,8 @@ repositories {
 sourceCompatibility = 1.7
 targetCompatibility = 1.7
 
-def version = Jvm.current().javaVersion
-if (version.java8Compatible || version.java9Compatible) {
+def javaVersion = Jvm.current().javaVersion
+if (javaVersion.java8Compatible || javaVersion.java9Compatible) {
     if (hasProperty("rt.17.jar")) {
         // Solution for bootstrap classpath warning and possible issues with compatibility with 1.7 libraries
         // was taken from this post on discuss.gradle.org: http://bit.ly/24xD9j0
@@ -118,8 +118,8 @@ if (version.java8Compatible || version.java9Compatible) {
         logger.warn "\n"
         logger.warn "In some isolated instances, using a bootstrap library from a JDK version later than 1.7 can result in run-time errors.\n"
     }
-} else if (!version.java7Compatible) {
-    throw new BuildCancelledException("You are using a JDK version (${version}) that is not compatible with Java 7. The XNAT build will fail. Please install a JDK version of Java 7 or later.")
+} else if (!javaVersion.java7Compatible) {
+    throw new BuildCancelledException("You are using a JDK version (${javaVersion}) that is not compatible with Java 7. The XNAT build will fail. Please install a JDK version of Java 7 or later.")
 }
 
 if (hasProperty("archiveName")) {
@@ -191,7 +191,7 @@ war {
     }
 }
 jacoco {
-    toolVersion = "0.7.6.201602180812"
+    toolVersion = "0.7.7.201606060606"
 }
 
 jacocoTestReport {
@@ -465,7 +465,7 @@ dependencies {
     runtime "org.codehaus.groovy.modules.http-builder:http-builder:0.7.2"
     runtime "net.imagej:ij:1.50e"
     runtime "net.bull.javamelody:javamelody-core:1.58.0"
-    runtime "org.javassist:javassist:3.20.0-GA"
+    runtime "org.javassist:javassist:3.21.0-GA"
 
     providedCompile "javax.servlet:javax.servlet-api:${vServletApi}"
 
diff --git a/src/main/java/org/nrg/dcm/id/ClassicDicomObjectIdentifier.java b/src/main/java/org/nrg/dcm/id/ClassicDicomObjectIdentifier.java
index 628f3aa02fe71beae3b53d9a95023d7977d87674..34b19420c88d3d664affb7fb24a7fade149288f9 100644
--- a/src/main/java/org/nrg/dcm/id/ClassicDicomObjectIdentifier.java
+++ b/src/main/java/org/nrg/dcm/id/ClassicDicomObjectIdentifier.java
@@ -19,31 +19,19 @@ import java.util.List;
 import java.util.regex.Pattern;
 
 public class ClassicDicomObjectIdentifier extends CompositeDicomObjectIdentifier {
-    private static final ImmutableList<Extractor> aaExtractors, sessionExtractors, subjectExtractors;
-    static {
-        final ImmutableList.Builder<Extractor> aab = new ImmutableList.Builder<Extractor>();
-        aab.add(new ContainedAssignmentExtractor(Tag.PatientComments, "AA", Pattern.CASE_INSENSITIVE),
-                new ContainedAssignmentExtractor(Tag.StudyComments, "AA", Pattern.CASE_INSENSITIVE));
-        aaExtractors = aab.build();
-
-        final ImmutableList.Builder<Extractor> sessb = new ImmutableList.Builder<Extractor>();
-        sessb.add(new ContainedAssignmentExtractor(Tag.PatientComments, "Session", Pattern.CASE_INSENSITIVE),
-                new ContainedAssignmentExtractor(Tag.StudyComments, "Session", Pattern.CASE_INSENSITIVE),
-                new TextExtractor(Tag.PatientID));
-        sessionExtractors = sessb.build();
-
-        final ImmutableList.Builder<Extractor> subjb = new ImmutableList.Builder<Extractor>();
-        subjb.add(new ContainedAssignmentExtractor(Tag.PatientComments, "Subject", Pattern.CASE_INSENSITIVE),
-                new ContainedAssignmentExtractor(Tag.StudyComments, "Subject", Pattern.CASE_INSENSITIVE),
-                new TextExtractor(Tag.PatientName));
-        subjectExtractors = subjb.build();
-    }
-
-    public static List<Extractor> getAAExtractors() { return aaExtractors; }
-    public static List<Extractor> getSessionExtractors() { return sessionExtractors; }
-    public static List<Extractor> getSubjectExtractors() { return subjectExtractors; }
+    private static final ImmutableList<Extractor> attributeExtractors = new ImmutableList.Builder<Extractor>().add(new ContainedAssignmentExtractor(Tag.PatientComments, "AA", Pattern.CASE_INSENSITIVE))
+                                                                                                              .add(new ContainedAssignmentExtractor(Tag.StudyComments, "AA", Pattern.CASE_INSENSITIVE))
+                                                                                                              .build();
+    private static final ImmutableList<Extractor> sessionExtractors   = new ImmutableList.Builder<Extractor>().add(new ContainedAssignmentExtractor(Tag.PatientComments, "Session", Pattern.CASE_INSENSITIVE))
+                                                                                                              .add(new ContainedAssignmentExtractor(Tag.StudyComments, "Session", Pattern.CASE_INSENSITIVE))
+                                                                                                              .add(new TextExtractor(Tag.PatientID))
+                                                                                                              .build();
+    private static final ImmutableList<Extractor> subjectExtractors   = new ImmutableList.Builder<Extractor>().add(new ContainedAssignmentExtractor(Tag.PatientComments, "Subject", Pattern.CASE_INSENSITIVE))
+                                                                                                              .add(new ContainedAssignmentExtractor(Tag.StudyComments, "Subject", Pattern.CASE_INSENSITIVE))
+                                                                                                              .add(new TextExtractor(Tag.PatientName))
+                                                                                                              .build();
 
     public ClassicDicomObjectIdentifier() {
-        super(new Xnat15DicomProjectIdentifier(), subjectExtractors, sessionExtractors, aaExtractors);
+        super(new Xnat15DicomProjectIdentifier(), subjectExtractors, sessionExtractors, attributeExtractors);
     }
 }
diff --git a/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java b/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java
index f7db6317a8b34ae2130778afccb87a5507b7f07e..cea37377703e06e0ce0081ee8a042cd69d5b7740 100644
--- a/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java
+++ b/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java
@@ -156,19 +156,12 @@ public class XnatPipelineLauncher {
     }
 
     public XnatPipelineLauncher(RunData data, Context context) {
-        user = XDAT.getUserDetails();
-        host = TurbineUtils.GetFullServerPath();
-        startAt = null;
-        supressNotification = false;
-        this.needsBuildDir = true;
-        waitFor = false;
-        addUserEmailForNotification();
-        parameterFile = null;
+        this(XDAT.getUserDetails());
     }
 
     public XnatPipelineLauncher(UserI user) {
         this.user = user;
-        host = TurbineUtils.GetFullServerPath();
+        host = getHostFromSiteConfig();
         startAt = null;
         supressNotification = false;
         waitFor = false;
@@ -177,6 +170,11 @@ public class XnatPipelineLauncher {
         parameterFile = null;
     }
 
+    private String getHostFromSiteConfig() {
+        final String pipelineUrl = XDAT.safeSiteConfigProperty("processingUrl", "");
+        return StringUtils.isNotBlank(pipelineUrl) ? pipelineUrl : TurbineUtils.GetFullServerPath();
+    }
+
     private void addUserEmailForNotification() {
         notify(user.getEmail());
         notify(XDAT.getSiteConfigPreferences().getAdminEmail());
@@ -623,7 +621,7 @@ public class XnatPipelineLauncher {
         xnatPipelineLauncher.setParameter("useremail", user.getEmail());
         xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
         xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
-        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
         xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
 
         xnatPipelineLauncher.setId(imageSession.getId());
@@ -653,24 +651,6 @@ public class XnatPipelineLauncher {
         return xnatPipelineLauncher;
     }
 
-    public static XnatPipelineLauncher GetBareLauncherForExperiment(RunData data, Context context, XnatExperimentdata imageSession) throws Exception {
-        XnatPipelineLauncher xnatPipelineLauncher = new XnatPipelineLauncher(data, context);
-        xnatPipelineLauncher.setSupressNotification(true);
-        UserI user = XDAT.getUserDetails();
-        xnatPipelineLauncher.setParameter("useremail", user.getEmail());
-        xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
-        xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
-        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
-        xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
-
-        xnatPipelineLauncher.setId(imageSession.getId());
-        xnatPipelineLauncher.setLabel(imageSession.getLabel());
-        xnatPipelineLauncher.setDataType(imageSession.getXSIType());
-        xnatPipelineLauncher.setExternalId(imageSession.getProject());
-
-        return xnatPipelineLauncher;
-    }
-
     public static XnatPipelineLauncher GetLauncher(RunData data, Context context, XnatImagesessiondata imageSession) throws Exception {
         XnatPipelineLauncher xnatPipelineLauncher = GetLauncherForExperiment(data, context, imageSession);
         String path = imageSession.getArchivePath();
diff --git a/src/main/java/org/nrg/viewer/QCImageCreator.java b/src/main/java/org/nrg/viewer/QCImageCreator.java
index fc1fbec6d293c4a444ca399997a00ab4dc8931f0..5e252b89103ba269b7b3106692b394613804816b 100644
--- a/src/main/java/org/nrg/viewer/QCImageCreator.java
+++ b/src/main/java/org/nrg/viewer/QCImageCreator.java
@@ -49,7 +49,7 @@ public class QCImageCreator {
         xnatPipelineLauncher.setParameter("session", mrSession.getId() );
         xnatPipelineLauncher.setParameter("notify", "0" );
 	    xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
-	    xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+	    xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
 	    xnatPipelineLauncher.setParameter("useremail", user.getEmail());
 	    xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
 
diff --git a/src/main/java/org/nrg/xapi/exceptions/InsufficientPrivilegesException.java b/src/main/java/org/nrg/xapi/exceptions/InsufficientPrivilegesException.java
new file mode 100644
index 0000000000000000000000000000000000000000..83f82face5dcf99744bbe8f4b376152b93df74cf
--- /dev/null
+++ b/src/main/java/org/nrg/xapi/exceptions/InsufficientPrivilegesException.java
@@ -0,0 +1,25 @@
+/*
+ * web: org.nrg.xapi.exceptions.DataFormatException
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xapi.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.BAD_REQUEST)
+public class InsufficientPrivilegesException extends ApiException {
+    public InsufficientPrivilegesException(final String username) {
+        super(HttpStatus.BAD_REQUEST.value(), username);
+    }
+
+    @Override
+    public String getMessage() {
+        return String.format("The user %s has insufficient privileges for the requested operation.", super.getMessage());
+    }
+}
diff --git a/src/main/java/org/nrg/xapi/rest/XapiRestControllerAdvice.java b/src/main/java/org/nrg/xapi/rest/XapiRestControllerAdvice.java
index 9fe567039181f4b2048f236ff6d5c8e9884cda61..14a04d5ebe62e6f4e24fb4d5bd670c308bd1b1a6 100644
--- a/src/main/java/org/nrg/xapi/rest/XapiRestControllerAdvice.java
+++ b/src/main/java/org/nrg/xapi/rest/XapiRestControllerAdvice.java
@@ -9,12 +9,16 @@
 
 package org.nrg.xapi.rest;
 
+import org.nrg.action.ClientException;
+import org.nrg.action.ServerException;
 import org.nrg.config.exceptions.ConfigServiceException;
 import org.nrg.dcm.exceptions.EnabledDICOMReceiverWithDuplicatePortException;
 import org.nrg.framework.exceptions.NrgServiceError;
 import org.nrg.framework.exceptions.NrgServiceException;
 import org.nrg.framework.exceptions.NrgServiceRuntimeException;
 import org.nrg.xapi.exceptions.DataFormatException;
+import org.nrg.xapi.exceptions.InsufficientPrivilegesException;
+import org.nrg.xapi.exceptions.NotFoundException;
 import org.nrg.xapi.exceptions.ResourceAlreadyExistsException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +47,11 @@ public class XapiRestControllerAdvice {
         return handleException(request, exception.getMessage(), exception);
     }
 
+    @ExceptionHandler(InsufficientPrivilegesException.class)
+    public ModelAndView handleInsufficientPrivilegesException(final HttpServletRequest request, final DataFormatException exception) {
+        return handleException(request, exception.getMessage(), exception);
+    }
+
     @ExceptionHandler(ResourceAlreadyExistsException.class)
     public ModelAndView handleDataFormatException(final HttpServletRequest request, final ResourceAlreadyExistsException exception) {
         return handleException(request, exception.getMessage(), exception);
@@ -64,11 +73,26 @@ public class XapiRestControllerAdvice {
         return handleException(HttpStatus.BAD_REQUEST, request, "Unable to find requested file or resource: " + exception.getMessage(), exception);
     }
 
+    @ExceptionHandler(NotFoundException.class)
+    public ModelAndView handleNotFoundException(final HttpServletRequest request, final NotFoundException exception) {
+        return handleException(HttpStatus.BAD_REQUEST, request, exception.getMessage(), exception);
+    }
+
     @ExceptionHandler(ConfigServiceException.class)
     public ModelAndView handleConfigServiceException(final HttpServletRequest request, final ConfigServiceException exception) {
         return handleException(HttpStatus.INTERNAL_SERVER_ERROR, request, "An error occurred when accessing the configuration service: " + exception.getMessage(), exception);
     }
 
+    @ExceptionHandler(ServerException.class)
+    public ModelAndView handleServerException(final HttpServletRequest request, final ConfigServiceException exception) {
+        return handleException(HttpStatus.INTERNAL_SERVER_ERROR, request, "An error occurred on the server during the request: " + exception.getMessage(), exception);
+    }
+
+    @ExceptionHandler(ClientException.class)
+    public ModelAndView handleClientException(final HttpServletRequest request, final ConfigServiceException exception) {
+        return handleException(HttpStatus.BAD_REQUEST, request, "There was an error in the request: " + exception.getMessage(), exception);
+    }
+
     private ModelAndView handleException(final HttpServletRequest request, final String message) {
         return handleException(request, message, null);
     }
diff --git a/src/main/java/org/nrg/xapi/rest/data/CatalogApi.java b/src/main/java/org/nrg/xapi/rest/data/CatalogApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc155323fad32337e6da9f0db663280582074f8b
--- /dev/null
+++ b/src/main/java/org/nrg/xapi/rest/data/CatalogApi.java
@@ -0,0 +1,74 @@
+/*
+ * web: org.nrg.xapi.rest.data.InvestigatorsApi
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xapi.rest.data;
+
+import com.google.common.base.Joiner;
+import io.swagger.annotations.*;
+import org.nrg.action.ClientException;
+import org.nrg.action.ServerException;
+import org.nrg.framework.annotations.XapiRestController;
+import org.nrg.xdat.rest.AbstractXapiRestController;
+import org.nrg.xdat.security.services.RoleHolder;
+import org.nrg.xdat.security.services.UserManagementServiceI;
+import org.nrg.xft.security.UserI;
+import org.nrg.xnat.services.archive.CatalogService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Api(description = "XNAT Archive and Resource Management API")
+@XapiRestController
+@RequestMapping(value = "/archive")
+public class CatalogApi extends AbstractXapiRestController {
+    @Autowired
+    public CatalogApi(final UserManagementServiceI userManagementService, final RoleHolder roleHolder, final CatalogService service) {
+        super(userManagementService, roleHolder);
+        _service = service;
+    }
+
+    @ApiOperation(value = "Refresh the catalog entry for one or more resources.", notes = "The resource should be identified by standard archive-relative paths, such as /archive/experiments/XNAT_E0001 or /archive/projects/XNAT_01/subjects/XNAT_01_01.", response = Void.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "The refresh operation(s) were completed successfully."),
+                   @ApiResponse(code = 500, message = "An unexpected or unknown error occurred")})
+    @RequestMapping(value = "catalogs/refresh", consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
+    @ResponseBody
+    public ResponseEntity<Void> refreshResourceCatalog(@ApiParam("The list of resources to be refreshed.") @RequestBody final List<String> resources) throws ServerException, ClientException {
+        return refreshResourceCatalogWithOptions(null, resources);
+    }
+
+    @ApiOperation(value = "Refresh the catalog entry for one or more resources, performing only the operations specified.", notes = "The resource should be identified by standard archive-relative paths, such as /archive/experiments/XNAT_E0001 or /archive/projects/XNAT_01/subjects/XNAT_01_01. The available operations are All, Append, Checksum, Delete, and PopulateStats. They should be comma separated but without spaces. Omitting the operations implies All.", response = Void.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "The refresh operation(s) were completed successfully."),
+                   @ApiResponse(code = 500, message = "An unexpected or unknown error occurred")})
+    @RequestMapping(value = "catalogs/refresh/{operations}", consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
+    @ResponseBody
+    public ResponseEntity<Void> refreshResourceCatalogWithOptions(
+            @ApiParam("The operations to be performed") @PathVariable final List<CatalogService.Operation> operations,
+            @ApiParam("The list of resources to be refreshed.") @RequestBody final List<String> resources) throws ServerException, ClientException {
+        final UserI user = getSessionUser();
+
+        _log.info("User {} requested catalog refresh for the following resources: " + Joiner.on(", ").join(resources));
+        if (operations == null) {
+            _service.refreshResourceCatalogs(user, resources);
+        } else {
+            _service.refreshResourceCatalogs(user, resources, operations);
+        }
+
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    private static final Logger _log = LoggerFactory.getLogger(CatalogApi.class);
+
+    private final CatalogService _service;
+}
diff --git a/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java b/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
index 8e4577e3ef2bcaa59a63e3923eb5bdbbd6af344b..a748afe9bc02e272e0131a09729b9b54c05276be 100644
--- a/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
+++ b/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
@@ -12,12 +12,11 @@ package org.nrg.xapi.rest.notifications;
 import io.swagger.annotations.*;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.framework.annotations.XapiRestController;
-import org.nrg.framework.exceptions.NrgServiceError;
-import org.nrg.framework.exceptions.NrgServiceRuntimeException;
 import org.nrg.framework.services.SerializerService;
 import org.nrg.prefs.exceptions.InvalidPreferenceName;
 import org.nrg.xapi.exceptions.InitializationException;
 import org.nrg.xdat.preferences.NotificationsPreferences;
+import org.nrg.xdat.preferences.SmtpServer;
 import org.nrg.xdat.rest.AbstractXapiRestController;
 import org.nrg.xdat.security.services.RoleHolder;
 import org.nrg.xdat.security.services.UserManagementServiceI;
@@ -28,7 +27,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
 import org.springframework.web.bind.annotation.*;
 
 import javax.inject.Inject;
@@ -58,10 +56,9 @@ public class NotificationsApi extends AbstractXapiRestController {
                                                        + "by calling the POST version of this method.";
 
     @Inject
-    public NotificationsApi(final UserManagementServiceI userManagementService, final RoleHolder roleHolder, final NotificationsPreferences notificationsPrefs, final JavaMailSenderImpl javaMailSender, final XnatAppInfo appInfo, final SerializerService serializer) {
+    public NotificationsApi(final UserManagementServiceI userManagementService, final RoleHolder roleHolder, final NotificationsPreferences notificationsPrefs, final XnatAppInfo appInfo, final SerializerService serializer) {
         super(userManagementService, roleHolder);
         _notificationsPrefs = notificationsPrefs;
-        _javaMailSender = javaMailSender;
         _appInfo = appInfo;
         _serializer = serializer;
     }
@@ -124,7 +121,7 @@ public class NotificationsApi extends AbstractXapiRestController {
                 } else if (StringUtils.equals(name, "emailRecipientUpdate")) {
                     _notificationsPrefs.setEmailRecipientUpdate(properties.getProperty(name));
                 } else if (StringUtils.equals(name, "smtpServer")) {
-                    _notificationsPrefs.setSmtpServer(_serializer.deserializeJson(properties.getProperty(name), SerializerService.TYPE_REF_MAP_STRING_STRING));
+                    _notificationsPrefs.setSmtpServer(_serializer.deserializeJson(properties.getProperty(name), SmtpServer.class));
                 } else {
                     _notificationsPrefs.set(properties.getProperty(name), name);
                 }
@@ -137,32 +134,12 @@ public class NotificationsApi extends AbstractXapiRestController {
                 _log.error("An error occurred deserializing the preference: " + name + ", which is just lame.");
             }
         }
-
-        // If any of the SMTP properties changed, then change the values for
-        if (properties.containsKey("smtpServer") || properties.containsKey("host") || properties.containsKey("port") || properties.containsKey("protocol") || properties.containsKey("username") || properties.containsKey("password")) {
-            final String host     = _notificationsPrefs.getHostname();
-            final int    port     = _notificationsPrefs.getPort();
-            final String protocol = _notificationsPrefs.getProtocol();
-            final String username = _notificationsPrefs.getUsername();
-            final String password = _notificationsPrefs.getPassword();
-
-            logConfigurationSubmit(host, port, protocol, username, password, properties);
-
-            setHost(host, false);
-            setPort(port);
-            setProtocol(protocol);
-            setUsername(username);
-            setPassword(password);
-
-            _javaMailSender.setJavaMailProperties(getSubmittedProperties(properties));
-        }
-
         return new ResponseEntity<>(HttpStatus.OK);
     }
 
     @ApiOperation(value = "Returns the full SMTP server configuration.",
-                  notes = "Returns the configuration as a map of the standard Java mail sender properties&ndash;host, port, protocol, username, and password&ndash;along with any extended properties required for the configuration, e.g. configuring SSL- or TLS-secured SMTP services.",
-                  response = Properties.class)
+            notes = "Returns the configuration as a map of the standard Java mail sender properties&ndash;host, port, protocol, username, and password&ndash;along with any extended properties required for the configuration, e.g. configuring SSL- or TLS-secured SMTP services.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "SMTP service configuration properties successfully retrieved."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set site configuration properties."),
@@ -176,39 +153,18 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isDebugEnabled()) {
             _log.debug("User " + getSessionUser().getUsername() + " requested the site SMTP service configuration.");
         }
-        final Properties properties = new Properties();
-        properties.setProperty("host", _javaMailSender.getHost());
-        final int port = _javaMailSender.getPort();
-        if (port > 0) {
-            properties.setProperty("port", Integer.toString(port));
-        }
-        final String protocol = _javaMailSender.getProtocol();
-        if (StringUtils.isNotBlank(protocol)) {
-            properties.setProperty("protocol", protocol);
-        }
-        final String username = _javaMailSender.getUsername();
-        if (StringUtils.isNotBlank(username)) {
-            properties.setProperty("username", username);
-        }
-        final String password = _javaMailSender.getPassword();
-        if (StringUtils.isNotBlank(password)) {
-            properties.setProperty("password", password);
-        }
-        for (final String property : _javaMailSender.getJavaMailProperties().stringPropertyNames()) {
-            properties.setProperty(property, _javaMailSender.getJavaMailProperties().getProperty(property));
-        }
-        return new ResponseEntity<>(properties, HttpStatus.OK);
+        return new ResponseEntity<>(_notificationsPrefs.getSmtpServer().asProperties(), HttpStatus.OK);
     }
 
     @ApiOperation(value = "Sets the mail service properties. This return the SMTP server configuration as it exists after being set.",
-                  notes = POST_PROPERTIES_NOTES,
-                  response = Properties.class)
+            notes = POST_PROPERTIES_NOTES,
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service properties successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service properties."),
                    @ApiResponse(code = 500, message = "Unexpected error")})
     @RequestMapping(value = {"smtp"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.POST})
-    public ResponseEntity<Properties> setAllMailProperties(@ApiParam("The value to set for the email host.") @RequestParam(value = "host", required = false, defaultValue = NOT_SET) final String host,
+    public ResponseEntity<Properties> setAllMailProperties(@ApiParam("The value to set for the email host.") @RequestParam(value = "hostname", required = false, defaultValue = NOT_SET) final String hostname,
                                                            @ApiParam("The value to set for the email port.") @RequestParam(value = "port", required = false, defaultValue = "-1") final int port,
                                                            @ApiParam("The value to set for the email username.") @RequestParam(value = "username", required = false, defaultValue = NOT_SET) final String username,
                                                            @ApiParam("The value to set for the email password.") @RequestParam(value = "password", required = false, defaultValue = NOT_SET) final String password,
@@ -220,28 +176,22 @@ public class NotificationsApi extends AbstractXapiRestController {
         }
 
         cleanProperties(properties);
-        logConfigurationSubmit(host, port, username, password, protocol, properties);
+        logConfigurationSubmit(hostname, port, username, password, protocol, properties);
 
-        setHost(host, true);
-        setPort(port);
-        setProtocol(protocol);
-        setUsername(username);
-        setPassword(password);
-
-        _javaMailSender.setJavaMailProperties(getSubmittedProperties(properties));
+        _notificationsPrefs.setSmtpServer(new SmtpServer(hostname, port, protocol, username, password, properties));
 
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the submitted mail service properties. This returns the SMTP server configuration as it exists after being set.",
-                  notes = PUT_PROPERTIES_NOTES,
-                  response = Properties.class)
+            notes = PUT_PROPERTIES_NOTES,
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service properties successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service properties."),
                    @ApiResponse(code = 500, message = "Unexpected error")})
     @RequestMapping(value = {"smtp"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
-    public ResponseEntity<Properties> setSubmittedMailProperties(@ApiParam("The value to set for the email host.") @RequestParam(value = "host", required = false, defaultValue = NOT_SET) final String host,
+    public ResponseEntity<Properties> setSubmittedMailProperties(@ApiParam("The value to set for the email host.") @RequestParam(value = "hostname", required = false, defaultValue = NOT_SET) final String host,
                                                                  @ApiParam("The value to set for the email port.") @RequestParam(value = "port", required = false, defaultValue = "-1") final int port,
                                                                  @ApiParam("The value to set for the email username.") @RequestParam(value = "username", required = false, defaultValue = NOT_SET) final String username,
                                                                  @ApiParam("The value to set for the email password.") @RequestParam(value = "password", required = false, defaultValue = NOT_SET) final String password,
@@ -254,23 +204,14 @@ public class NotificationsApi extends AbstractXapiRestController {
 
         logConfigurationSubmit(host, port, username, password, protocol, properties);
 
-        setHost(host, false);
-        setPort(port);
-        setProtocol(protocol);
-        setUsername(username);
-        setPassword(password);
-        if (properties != null) {
-            for (final String property : properties.stringPropertyNames()) {
-                _javaMailSender.getJavaMailProperties().setProperty(property, properties.getProperty(property));
-            }
-        }
+        _notificationsPrefs.setSmtpServer(new SmtpServer(host, port, username, password, protocol, properties));
 
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the mail service host.",
-                  notes = "Sets the mail service host.",
-                  response = Properties.class)
+            notes = "Sets the mail service host.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service host successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service host."),
@@ -284,13 +225,13 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail host to: " + host);
         }
-        setHost(host, true);
+        _notificationsPrefs.setSmtpHostname(host);
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the mail service port.",
-                  notes = "Sets the mail service port.",
-                  response = Properties.class)
+            notes = "Sets the mail service port.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service port successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service port."),
@@ -304,13 +245,13 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail port to: " + port);
         }
-        setPort(port);
+        _notificationsPrefs.setSmtpPort(port);
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the mail service protocol.",
-                  notes = "Sets the mail service protocol.",
-                  response = Properties.class)
+            notes = "Sets the mail service protocol.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service protocol successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service protocol."),
@@ -324,13 +265,13 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail protocol to: " + protocol);
         }
-        setProtocol(protocol);
+        _notificationsPrefs.setSmtpProtocol(protocol);
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the mail service username.",
-                  notes = "Sets the mail service username.",
-                  response = Properties.class)
+            notes = "Sets the mail service username.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service username successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service username."),
@@ -344,13 +285,13 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail username to: " + username);
         }
-        setUsername(username);
+        _notificationsPrefs.setSmtpUsername(username);
         return getSmtpServerProperties();
     }
 
     @ApiOperation(value = "Sets the mail service password.",
-                  notes = "Sets the mail service password.",
-                  response = Properties.class)
+            notes = "Sets the mail service password.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service password successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service password."),
@@ -364,20 +305,56 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail password to: ********");
         }
-        setPassword(password);
+        _notificationsPrefs.setSmtpPassword(password);
         return getSmtpServerProperties();
     }
 
+    @ApiOperation(value = "Gets the value for a specified Java mail property.",
+            notes = "The value is always returned as a string.",
+            response = String.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "Property found and returned."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "Not authorized to set the mail service properties."),
+                   @ApiResponse(code = 404, message = "Specified key not found in the mail service properties."),
+                   @ApiResponse(code = 500, message = "Unexpected error")})
+    @RequestMapping(value = {"smtp/property/{property}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET})
+    public ResponseEntity<String> getExtendedProperty(@ApiParam(value = "The mail property to be retrieved.", required = true) @PathVariable final String property) {
+        final HttpStatus status = isPermitted();
+        if (status != null) {
+            return new ResponseEntity<>(status);
+        }
+        if (!_notificationsPrefs.getSmtpServer().getMailProperties().containsKey(property)) {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+        _log.info("User {} retrieving value for property {}", getSessionUser().getLogin(), property);
+        return new ResponseEntity<>(_notificationsPrefs.getSmtpMailProperty(property), HttpStatus.OK);
+    }
+
     @ApiOperation(value = "Sets a Java mail property with the submitted name and value.",
-                  notes = "Setting a property to an empty value will remove the property.",
-                  response = Properties.class)
+            notes = "Setting a property to an existing value will overwrite the existing value. The value returned by this function contains the previous value (if any).",
+            response = String.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "Property found and returned."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "Not authorized to set the mail service properties."),
+                   @ApiResponse(code = 404, message = "Specified key not found in the mail service properties."),
+                   @ApiResponse(code = 500, message = "Unexpected error")})
+    @RequestMapping(value = {"smtp/property/{property}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    public ResponseEntity<String> setExtendedProperty(@ApiParam(value = "The name of the extended mail property to set.", required = true) @PathVariable final String property,
+                                                      @ApiParam(value = "The value to set for the extended mail property.", required = true) @RequestBody final String value) {
+        return setExtendedPropertyFromPath(property, value);
+    }
+
+    @ApiOperation(value = "Sets a Java mail property with the submitted name and value.",
+            notes = "Setting a property to an existing value will overwrite the existing value. The value returned by this function contains the previous value (if any).",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Mail service password successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the mail service password."),
+                   @ApiResponse(code = 404, message = "Specified key not found in the mail service properties."),
                    @ApiResponse(code = 500, message = "Unexpected error")})
     @RequestMapping(value = {"smtp/property/{property}/{value}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
-    public ResponseEntity<Properties> setExtendedProperty(@ApiParam(value = "The value to set for the email password.", required = true) @PathVariable final String property,
-                                                          @ApiParam(value = "The value to set for the email password.", required = true) @PathVariable final String value) {
+    public ResponseEntity<String> setExtendedPropertyFromPath(@ApiParam(value = "The name of the extended mail property to set.", required = true) @PathVariable final String property,
+                                                              @ApiParam(value = "The value to set for the extended mail property.", required = true) @PathVariable final String value) {
         final HttpStatus status = isPermitted();
         if (status != null) {
             return new ResponseEntity<>(status);
@@ -385,13 +362,33 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (_log.isInfoEnabled()) {
             _log.info("User " + getSessionUser().getLogin() + " setting mail password to: ********");
         }
-        setProperty(property, value);
-        return getSmtpServerProperties();
+        return new ResponseEntity<>(_notificationsPrefs.setSmtpMailProperty(property, value), HttpStatus.OK);
+    }
+
+    @ApiOperation(value = "Removes the value for a specified Java mail property.",
+            notes = "This completely removes the specified mail property. The value returned by this function contains the previous value (if any).",
+            response = String.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "Property found and returned."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "Not authorized to set the mail service properties."),
+                   @ApiResponse(code = 404, message = "Specified key not found in the mail service properties."),
+                   @ApiResponse(code = 500, message = "Unexpected error")})
+    @RequestMapping(value = "smtp/property/{property}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.DELETE)
+    public ResponseEntity<String> removeExtendedProperty(@ApiParam(value = "The mail property to be removed.", required = true) @PathVariable final String property) {
+        final HttpStatus status = isPermitted();
+        if (status != null) {
+            return new ResponseEntity<>(status);
+        }
+        if (!_notificationsPrefs.getSmtpServer().getMailProperties().containsKey(property)) {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+        _log.info("User {} removing value for property {}", getSessionUser().getLogin(), property);
+        return new ResponseEntity<>(_notificationsPrefs.removeSmtpMailProperty(property), HttpStatus.OK);
     }
 
     @ApiOperation(value = "Sets the email message for contacting help.",
-                  notes = "Sets the email message that people should receive when contacting help.",
-                  response = Void.class)
+            notes = "Sets the email message that people should receive when contacting help.",
+            response = Void.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Help email message successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the help email message."),
@@ -403,8 +400,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email message for user registration.",
-                  notes = "Sets the email message that people should receive when they register. Link for email validation is auto-populated.",
-                  response = Properties.class)
+            notes = "Sets the email message that people should receive when they register. Link for email validation is auto-populated.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "User registration email message successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the user registration email message."),
@@ -416,8 +413,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email message for forgot username.",
-                  notes = "Sets the email message that people should receive when they click that they forgot their username.",
-                  response = Properties.class)
+            notes = "Sets the email message that people should receive when they click that they forgot their username.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Forgot username email message successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the forgot username email message."),
@@ -429,8 +426,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email message for password reset.",
-                  notes = "Sets the email message that people should receive when they click to reset their password.  Link for password reset is auto-populated.",
-                  response = Properties.class)
+            notes = "Sets the email message that people should receive when they click to reset their password.  Link for password reset is auto-populated.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Password reset message successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the password reset message."),
@@ -442,8 +439,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns the email message for contacting help.",
-                  notes = "This returns the email message that people should receive when contacting help.",
-                  response = String.class)
+            notes = "This returns the email message that people should receive when contacting help.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for contacting help successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for contacting help."),
@@ -454,8 +451,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns the email message for user registration.",
-                  notes = "This returns the email message that people should receive when they register. Link for email validation is auto-populated.",
-                  response = String.class)
+            notes = "This returns the email message that people should receive when they register. Link for email validation is auto-populated.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for user registration successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for user registration."),
@@ -466,8 +463,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns the email message for forgot username.",
-                  notes = "This returns the email message that people should receive when they click that they forgot their username.",
-                  response = String.class)
+            notes = "This returns the email message that people should receive when they click that they forgot their username.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for forgot username successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for forgot username."),
@@ -478,8 +475,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns the email message for password reset.",
-                  notes = "This returns the email message that people should receive when they click to reset their password.  Link for password reset is auto-populated.",
-                  response = String.class)
+            notes = "This returns the email message that people should receive when they click to reset their password.  Link for password reset is auto-populated.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for password reset successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for password reset."),
@@ -490,8 +487,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets whether admins should be notified of user registration.",
-                  notes = "Sets whether admins should be notified of user registration.",
-                  response = Void.class)
+            notes = "Sets whether admins should be notified of user registration.",
+            response = Void.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether admins should be notified of user registration successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set whether admins should be notified of user registration."),
@@ -503,8 +500,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets whether admins should be notified of pipeline processing submit.",
-                  notes = "Sets whether admins should be notified of pipeline processing submit.",
-                  response = Properties.class)
+            notes = "Sets whether admins should be notified of pipeline processing submit.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether admins should be notified of pipeline processing submit successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set whether admins should be notified of pipeline processing submit."),
@@ -516,8 +513,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets whether admins should be notified of project access requests.",
-                  notes = "Sets whether admins should be notified of project access requests.",
-                  response = Properties.class)
+            notes = "Sets whether admins should be notified of project access requests.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether admins should be notified of project access requests successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set whether admins should be notified of project access requests."),
@@ -529,8 +526,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets whether admins should be notified of session transfer.",
-                  notes = "Sets whether admins should be notified of session transfer by user.",
-                  response = Properties.class)
+            notes = "Sets whether admins should be notified of session transfer by user.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether admins should be notified of session transfer successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set whether admins should be notified of session transfer."),
@@ -542,8 +539,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns whether admins should be notified of user registration.",
-                  notes = "This returns whether admins should be notified of user registration.",
-                  response = Boolean.class)
+            notes = "This returns whether admins should be notified of user registration.",
+            response = Boolean.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for contacting help successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for contacting help."),
@@ -554,8 +551,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns whether admins should be notified of pipeline processing submit.",
-                  notes = "This returns whether admins should be notified of pipeline processing submit.",
-                  response = Boolean.class)
+            notes = "This returns whether admins should be notified of pipeline processing submit.",
+            response = Boolean.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for user registration successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for user registration."),
@@ -566,8 +563,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns whether admins should be notified of project access requests.",
-                  notes = "This returns whether admins should be notified of project access requests.",
-                  response = Boolean.class)
+            notes = "This returns whether admins should be notified of project access requests.",
+            response = Boolean.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for forgot username successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for forgot username."),
@@ -578,8 +575,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns whether admins should be notified of session transfer.",
-                  notes = "This returns whether admins should be notified of session transfer.",
-                  response = Boolean.class)
+            notes = "This returns whether admins should be notified of session transfer.",
+            response = Boolean.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Email message for password reset successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get email message for password reset."),
@@ -590,8 +587,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets whether non-users should be able to subscribe to notifications.",
-                  notes = "Sets whether non-users should be able to subscribe to notifications.",
-                  response = Properties.class)
+            notes = "Sets whether non-users should be able to subscribe to notifications.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether non-users should be able to subscribe to notifications."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set whether non-users should be able to subscribe to notifications."),
@@ -603,8 +600,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns whether non-users should be able to subscribe to notifications.",
-                  notes = "This returns whether non-users should be able to subscribe to notifications.",
-                  response = Boolean.class)
+            notes = "This returns whether non-users should be able to subscribe to notifications.",
+            response = Boolean.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Whether non-users should be able to subscribe to notifications successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get whether non-users should be able to subscribe to notifications."),
@@ -615,8 +612,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email addresses for error notifications.",
-                  notes = "Sets the email addresses that should be subscribed to error notifications.",
-                  response = Void.class)
+            notes = "Sets the email addresses that should be subscribed to error notifications.",
+            response = Void.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Error subscribers successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the error subscribers."),
@@ -628,8 +625,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email addresses for issue notifications.",
-                  notes = "Sets the email addresses that should be subscribed to issue notifications.",
-                  response = Properties.class)
+            notes = "Sets the email addresses that should be subscribed to issue notifications.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Issue subscribers successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the issue subscribers."),
@@ -641,8 +638,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email addresses for new user notifications.",
-                  notes = "Sets the email addresses that should be subscribed to new user notifications.",
-                  response = Properties.class)
+            notes = "Sets the email addresses that should be subscribed to new user notifications.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "New user subscribers successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the new user subscribers."),
@@ -654,8 +651,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Sets the email addresses for update notifications.",
-                  notes = "Sets the email addresses that should be subscribed to update notifications.",
-                  response = Properties.class)
+            notes = "Sets the email addresses that should be subscribed to update notifications.",
+            response = Properties.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Update subscribers successfully set."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to set the update subscribers."),
@@ -667,8 +664,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns list of email addresses subscribed to error notifications.",
-                  notes = "This returns a list of all the email addresses that are subscribed to receive error notifications.",
-                  response = String.class)
+            notes = "This returns a list of all the email addresses that are subscribed to receive error notifications.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Error notification subscribers successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get subscribers for email notifications."),
@@ -679,8 +676,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns list of email addresses subscribed to issue notifications.",
-                  notes = "This returns a list of all the email addresses that are subscribed to receive issue notifications.",
-                  response = String.class)
+            notes = "This returns a list of all the email addresses that are subscribed to receive issue notifications.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Issue notification subscribers successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get subscribers for email notifications."),
@@ -691,8 +688,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns list of email addresses subscribed to new user notifications.",
-                  notes = "This returns a list of all the email addresses that are subscribed to receive new user notifications.",
-                  response = String.class)
+            notes = "This returns a list of all the email addresses that are subscribed to receive new user notifications.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "New user notification subscribers successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get subscribers for email notifications."),
@@ -703,8 +700,8 @@ public class NotificationsApi extends AbstractXapiRestController {
     }
 
     @ApiOperation(value = "Returns list of email addresses subscribed to update notifications.",
-                  notes = "This returns a list of all the email addresses that are subscribed to receive update notifications.",
-                  response = String.class)
+            notes = "This returns a list of all the email addresses that are subscribed to receive update notifications.",
+            response = String.class)
     @ApiResponses({@ApiResponse(code = 200, message = "Update notification subscribers successfully returned."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "Not authorized to get subscribers for email notifications."),
@@ -714,51 +711,27 @@ public class NotificationsApi extends AbstractXapiRestController {
         return new ResponseEntity<>(_notificationsPrefs.getEmailRecipientUpdate(), HttpStatus.OK);
     }
 
-    protected Properties getSubmittedProperties(final Properties properties) {
-        // Set all of the submitted properties.
-        final Properties javaMailProperties = new Properties();
-        if (properties != null) {
-            for (final String property : properties.stringPropertyNames()) {
-                final String value = properties.getProperty(property);
-                if (StringUtils.isNotBlank(value)) {
-                    javaMailProperties.setProperty(property, value);
-                }
-            }
-        }
-
-        // Find any existing properties that weren't submitted...
-        final Properties existing = _javaMailSender.getJavaMailProperties();
-        for (final String property : existing.stringPropertyNames()) {
-            if (!javaMailProperties.containsKey(property)) {
-                // Set the value to "", this will remove the property.
-                javaMailProperties.setProperty(property, "");
-            }
-        }
-        return javaMailProperties;
-    }
-
     private Properties convertToProperties(final Map<String, Object> preferenceMap) throws IOException {
         final Properties properties = new Properties();
         for (final String key : preferenceMap.keySet()) {
-            final Object object = preferenceMap.get(key);
-            String tempVal = "";
-            if(object!=null){
-                if(String.class.isAssignableFrom(object.getClass())){
-                    tempVal = (String)object;
-                }
-                else{
+            final Object object  = preferenceMap.get(key);
+            String       tempVal = "";
+            if (object != null) {
+                if (String.class.isAssignableFrom(object.getClass())) {
+                    tempVal = (String) object;
+                } else {
                     tempVal = _serializer.toJson(object);
                 }
             }
-            final String value  = tempVal;
+            final String value = tempVal;
             properties.setProperty(key, value);
         }
         return properties;
     }
 
     private void cleanProperties(final Properties properties) {
-        if (properties.containsKey("host")) {
-            properties.remove("host");
+        if (properties.containsKey("hostname")) {
+            properties.remove("hostname");
         }
         if (properties.containsKey("port")) {
             properties.remove("port");
@@ -772,47 +745,20 @@ public class NotificationsApi extends AbstractXapiRestController {
         if (properties.containsKey("password")) {
             properties.remove("password");
         }
-    }
-
-    private void setHost(final String host, final boolean freakOutIfBlank) {
-        if (freakOutIfBlank && StringUtils.isBlank(host)) {
-            throw new NrgServiceRuntimeException(NrgServiceError.ConfigurationError, "You can not set the SMTP server address to an empty value!");
-        }
-        if (StringUtils.isNotBlank(host)) {
-            _javaMailSender.setHost(host);
-        }
-    }
-
-    private void setPort(final int port) {
-        if (port != -1) {
-            _javaMailSender.setPort(port);
+        if (properties.containsKey("smtpHostname")) {
+            properties.remove("smtpHostname");
         }
-    }
-
-    private void setProtocol(final String protocol) {
-        if (!StringUtils.equals(NOT_SET, protocol)) {
-            _javaMailSender.setProtocol(protocol);
+        if (properties.containsKey("smtpPort")) {
+            properties.remove("smtpPort");
         }
-    }
-
-    private void setUsername(final String username) {
-        if (!StringUtils.equals(NOT_SET, username)) {
-            _javaMailSender.setUsername(username);
+        if (properties.containsKey("smtpProtocol")) {
+            properties.remove("smtpProtocol");
         }
-    }
-
-    private void setPassword(final String password) {
-        if (!StringUtils.equals(NOT_SET, password)) {
-            _javaMailSender.setPassword(password);
+        if (properties.containsKey("smtpUsername")) {
+            properties.remove("smtpUsername");
         }
-    }
-
-    private void setProperty(final String property, final String value) {
-        final Properties properties = _javaMailSender.getJavaMailProperties();
-        if (properties.containsKey(property) && StringUtils.isBlank(value)) {
-            properties.remove(property);
-        } else {
-            properties.setProperty(property, value);
+        if (properties.containsKey("smtpPassword")) {
+            properties.remove("smtpPassword");
         }
     }
 
@@ -834,11 +780,10 @@ public class NotificationsApi extends AbstractXapiRestController {
         }
     }
 
-    private static final Logger                                 _log                           = LoggerFactory.getLogger(NotificationsApi.class);
-    private static final String                                 NOT_SET                        = "NotSet";
+    private static final Logger _log    = LoggerFactory.getLogger(NotificationsApi.class);
+    private static final String NOT_SET = "NotSet";
 
     private final NotificationsPreferences _notificationsPrefs;
-    private final JavaMailSenderImpl       _javaMailSender;
     private final XnatAppInfo              _appInfo;
     private final SerializerService        _serializer;
 }
diff --git a/src/main/java/org/nrg/xapi/rest/settings/PreferencesApi.java b/src/main/java/org/nrg/xapi/rest/settings/PreferencesApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..89f7ca2d2ac3286442a537bb82c47227f4e64021
--- /dev/null
+++ b/src/main/java/org/nrg/xapi/rest/settings/PreferencesApi.java
@@ -0,0 +1,117 @@
+/*
+ * web: org.nrg.xapi.rest.settings.AutomationApi
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xapi.rest.settings;
+
+import com.google.common.collect.Maps;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.nrg.framework.annotations.XapiRestController;
+import org.nrg.prefs.beans.AbstractPreferenceBean;
+import org.nrg.xdat.rest.AbstractXapiRestController;
+import org.nrg.xdat.security.services.RoleHolder;
+import org.nrg.xdat.security.services.UserManagementServiceI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+@Api(description = "Preferences Service API")
+@XapiRestController
+@RequestMapping(value = "/prefs")
+public class PreferencesApi extends AbstractXapiRestController {
+    @Autowired
+    public PreferencesApi(final UserManagementServiceI userManagementService, final RoleHolder roleHolder, final List<AbstractPreferenceBean> preferences) {
+        super(userManagementService, roleHolder);
+        for (final AbstractPreferenceBean preferenceBean : preferences) {
+            _preferences.put(preferenceBean.getToolId(), preferenceBean);
+        }
+    }
+
+    @ApiOperation(value = "Returns the full map of preferences and values for this XNAT application.", response = Properties.class, responseContainer = "Map")
+    @ApiResponses({@ApiResponse(code = 200, message = "Preference settings successfully retrieved."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "Insufficient privileges to retrieve the requested setting."),
+                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
+    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
+    public ResponseEntity<Map<String, Properties>> getAllPreferenceSettings() {
+        if (_log.isDebugEnabled()) {
+            _log.info("User " + getSessionUser().getUsername() + " requested the system preference settings.");
+        }
+
+        final HttpStatus status = isPermitted();
+        if (status != null) {
+            return new ResponseEntity<>(status);
+        }
+
+        final Map<String, Properties> beanProperties = Maps.transformEntries(_preferences, new Maps.EntryTransformer<String, AbstractPreferenceBean, Properties>() {
+            @Override
+            public Properties transformEntry(@Nullable final String key, @Nullable final AbstractPreferenceBean value) {
+                return value != null ? value.asProperties() : EMPTY_PROPERTIES;
+            }
+        });
+        return new ResponseEntity<>(beanProperties, HttpStatus.OK);
+    }
+
+    @ApiOperation(value = "Returns the full map of preferences and values for this XNAT application.", response = String.class)
+    @ApiResponses({@ApiResponse(code = 200, message = "Preference settings successfully retrieved."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "Insufficient privileges to retrieve the requested setting."),
+                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
+    @RequestMapping(value = "ini", produces = MediaType.TEXT_PLAIN_VALUE, method = RequestMethod.GET)
+    public ResponseEntity<String> getPreferenceSettingsIni() {
+        if (_log.isDebugEnabled()) {
+            _log.info("User " + getSessionUser().getUsername() + " requested the system preference settings in ini format.");
+        }
+
+        final HttpStatus status = isPermitted();
+        if (status != null) {
+            return new ResponseEntity<>(status);
+        }
+
+        final Map<String, Properties> beanProperties = Maps.transformEntries(_preferences, new Maps.EntryTransformer<String, AbstractPreferenceBean, Properties>() {
+            @Override
+            public Properties transformEntry(@Nullable final String key, @Nullable final AbstractPreferenceBean value) {
+                return value != null ? value.asProperties() : EMPTY_PROPERTIES;
+            }
+        });
+
+        try (final StringWriter stringWriter = new StringWriter(); final PrintWriter writer = new PrintWriter(stringWriter)) {
+            for (final String toolId : beanProperties.keySet()) {
+                writer.println("[" + toolId + "]");
+                final Properties properties = beanProperties.get(toolId);
+                properties.store(writer, "Settings for tool " + toolId);
+                writer.println();
+            }
+            return new ResponseEntity<>(stringWriter.getBuffer().toString(), HttpStatus.OK);
+        } catch (IOException e) {
+            _log.error("An error occurred trying to write the preferences in ini format", e);
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    private static final Logger     _log             = LoggerFactory.getLogger(PreferencesApi.class);
+    private static final Properties EMPTY_PROPERTIES = new Properties();
+
+    private final Map<String, AbstractPreferenceBean> _preferences = Maps.newHashMap();
+}
diff --git a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java
index b45ddd15faa5362e3fe0405ee3e4182bdf363db2..708cc46249d6eb43cc3409d2eb321ab1d2ff73b2 100644
--- a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java
+++ b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java
@@ -13,14 +13,17 @@ import com.google.common.base.Joiner;
 import io.swagger.annotations.*;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.framework.annotations.XapiRestController;
+import org.nrg.framework.exceptions.NrgServiceRuntimeException;
 import org.nrg.prefs.exceptions.InvalidPreferenceName;
+import org.nrg.xapi.exceptions.ApiException;
 import org.nrg.xapi.exceptions.InitializationException;
+import org.nrg.xapi.exceptions.InsufficientPrivilegesException;
+import org.nrg.xapi.exceptions.NotFoundException;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xdat.rest.AbstractXapiRestController;
 import org.nrg.xdat.security.services.RoleHolder;
 import org.nrg.xdat.security.services.UserManagementServiceI;
 import org.nrg.xnat.services.XnatAppInfo;
-import org.nrg.xnat.turbine.utils.ArcSpecManager;
 import org.nrg.xnat.utils.XnatHttpUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -102,7 +105,7 @@ public class SiteConfigApi extends AbstractXapiRestController {
                    @ApiResponse(code = 403, message = "Not authorized to set site configuration properties."),
                    @ApiResponse(code = 500, message = "Unexpected error")})
     @RequestMapping(consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.POST)
-    public ResponseEntity<Void> setSiteConfigProperties(@ApiParam(value = "The map of site configuration properties to be set.", required = true) @RequestBody final Map<String, String> properties) throws InitializationException {
+    public ResponseEntity<Void> setSiteConfigProperties(@ApiParam(value = "The map of site configuration properties to be set.", required = true) @RequestBody final Map<String, String> properties) throws ApiException, InitializationException {
         final HttpStatus status = isPermitted();
         if (status != null) {
             return new ResponseEntity<>(status);
@@ -116,6 +119,12 @@ public class SiteConfigApi extends AbstractXapiRestController {
                 if (isInitializing && name.equals("initialized")) {
                     continue;
                 }
+                if (name.equals("receivedFileUser")) {
+                    final HttpStatus setStatus = setReceivedFileUser(properties.get(name));
+                    if (setStatus != null) {
+                        return new ResponseEntity<>(setStatus);
+                    }
+                }
                 _preferences.set(properties.get(name), name);
                 if (_log.isInfoEnabled()) {
                     _log.info("Set property {} to value: {}", name, properties.get(name));
@@ -127,10 +136,7 @@ public class SiteConfigApi extends AbstractXapiRestController {
 
         // If we're initializing...
         if (isInitializing) {
-            // Make the final initialization call.
-            initialize();
-
-            // Now make the initialized setting true.
+            // Now make the initialized setting true. This will kick off the initialized event handler.
             _preferences.setInitialized(true);
         }
 
@@ -191,7 +197,8 @@ public class SiteConfigApi extends AbstractXapiRestController {
                    @ApiResponse(code = 403, message = "Not authorized to set site configuration properties."),
                    @ApiResponse(code = 500, message = "Unexpected error")})
     @RequestMapping(value = "{property}", consumes = {MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
-    public ResponseEntity<Void> setSiteConfigProperty(@ApiParam(value = "The property to be set.", required = true) @PathVariable("property") final String property, @ApiParam("The value to be set for the property.") @RequestBody final String value) throws InitializationException {
+    public ResponseEntity<Void> setSiteConfigProperty(@ApiParam(value = "The property to be set.", required = true) @PathVariable("property") final String property,
+                                                      @ApiParam("The value to be set for the property.") @RequestBody final String value) throws InitializationException, ApiException {
         final HttpStatus status = isPermitted();
         if (status != null) {
             return new ResponseEntity<>(status);
@@ -202,8 +209,12 @@ public class SiteConfigApi extends AbstractXapiRestController {
         }
 
         if (StringUtils.equals("initialized", property) && StringUtils.equals("true", value)) {
-            initialize();
             _preferences.setInitialized(true);
+        } else if (StringUtils.equals("receivedFileUser", property)) {
+            final HttpStatus setStatus = setReceivedFileUser(value);
+            if (setStatus != null) {
+                return new ResponseEntity<>(setStatus);
+            }
         } else {
             try {
                 _preferences.set(value, property);
@@ -268,6 +279,24 @@ public class SiteConfigApi extends AbstractXapiRestController {
         return new ResponseEntity<>(_appInfo.getFormattedUptime(), HttpStatus.OK);
     }
 
+    private HttpStatus setReceivedFileUser(final String value) throws InsufficientPrivilegesException, NotFoundException {
+        try {
+            _preferences.setReceivedFileUser(value);
+        } catch (NrgServiceRuntimeException e) {
+            switch (e.getServiceError()) {
+                case PermissionsViolation:
+                    throw new InsufficientPrivilegesException(e.getMessage());
+                case UserNotFoundError:
+                    throw new NotFoundException("No user with the name " + e.getMessage() + " was found.");
+                case Unknown:
+                default:
+                    _log.error("An unknown error occurred trying to set the received file user to value " + value, e);
+                    return HttpStatus.INTERNAL_SERVER_ERROR;
+            }
+        }
+        return null;
+    }
+
     private Map<String, Object> getPreferences() {
         if (!_hasFoundPreferences) {
             return _preferences.getPreferenceMap();
@@ -277,17 +306,6 @@ public class SiteConfigApi extends AbstractXapiRestController {
         return preferences;
     }
 
-    private void initialize() throws InitializationException {
-        // In the case where the application hasn't yet been initialized, this operation should mean that the system is
-        // being initialized from the set-up page. In that case, we need to propagate a few properties to the arc-spec
-        // persistence to support
-        try {
-            ArcSpecManager.initialize(getSessionUser());
-        } catch (Exception e) {
-            throw new InitializationException(e);
-        }
-    }
-
     private static final Logger _log = LoggerFactory.getLogger(SiteConfigApi.class);
 
     private final SiteConfigPreferences _preferences;
diff --git a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java
index 248ccc1c70af79ff7f5d411e0cc6548acc29d68f..0f3412df29486d33ea110440649b4630339e21f3 100644
--- a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java
+++ b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java
@@ -134,20 +134,30 @@ public class UsersApi extends AbstractXapiRestController {
             } else {
                 username = principal.toString();
             }
-            final Map<String, Object> sessionData = new HashMap<>();
             final List<SessionInformation> sessions = _sessionRegistry.getAllSessions(principal, false);
+
+            // Sometimes there are no sessions, which is weird but OK, we don't want to see those entries.
+            final int count = sessions.size();
+            if (count == 0) {
+                continue;
+            }
+
+            // Now add user with a session or more to the list of active users.
             final ArrayList<String> sessionIds = new ArrayList<>();
             for (final SessionInformation session : sessions) {
                 sessionIds.add(session.getSessionId());
             }
+
+            final Map<String, Object> sessionData = new HashMap<>();
             sessionData.put("sessions", sessionIds);
-            sessionData.put("count", sessions.size());
+            sessionData.put("count", count);
+
             activeUsers.put(username, sessionData);
         }
         return new ResponseEntity<>(activeUsers, HttpStatus.OK);
     }
 
-    @ApiOperation(value = "Get information about active sessions for the indicated user.", notes = "Returns a map containing a list of session IDs usernames for users that have at least one currently active session, i.e. logged in or associated with a valid application session. This also includes the number of active sessions for each user.", response = String.class, responseContainer = "List")
+    @ApiOperation(value = "Get information about active sessions for the indicated user.", notes = "Returns a map containing a list of session IDs and usernames for users that have at least one currently active session, i.e. logged in or associated with a valid application session. This also includes the number of active sessions for each user.", response = String.class, responseContainer = "List")
     @ApiResponses({@ApiResponse(code = 200, message = "A list of active users."),
                    @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
                    @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
diff --git a/src/main/java/org/nrg/xnat/archive/Transfer.java b/src/main/java/org/nrg/xnat/archive/Transfer.java
index 367114e0ff833282c7d38412d25fd9cdfbaf1e1b..a8f6c11cebbf3bf790d00460145f6de65151a514 100644
--- a/src/main/java/org/nrg/xnat/archive/Transfer.java
+++ b/src/main/java/org/nrg/xnat/archive/Transfer.java
@@ -130,7 +130,7 @@ public class Transfer {
             xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
             xnatPipelineLauncher.setParameter("adminemail", admin_email);
             xnatPipelineLauncher.setParameter("xnatserver", system);
-            xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+            xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
             xnatPipelineLauncher.setParameter("sessionType", mr.getXSIType());
             xnatPipelineLauncher.setParameter("xnat_project", mr.getProject());
             xnatPipelineLauncher.setParameter("logDir", XDAT.getSiteConfigPreferences().getCachePath() + "logs" + "/" + "transfer");
diff --git a/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java b/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java
index 077b60d1b1dca036299bb2339d8013d01fb107b1..d8b0e24ae16d9c779311907d3531b8758f83389a 100644
--- a/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java
@@ -46,7 +46,7 @@ import java.util.List;
                 "org.nrg.xdat.daos", "org.nrg.xdat.services.impl.hibernate", "org.nrg.xft.daos",
                 "org.nrg.xft.event.listeners", "org.nrg.xft.services", "org.nrg.xnat.configuration",
                 "org.nrg.xnat.daos", "org.nrg.xnat.event.listeners", "org.nrg.xnat.helpers.merge",
-                "org.nrg.xnat.initialization.tasks", "org.nrg.xnat.services.impl.hibernate"})
+                "org.nrg.xnat.initialization.tasks"})
 @Import({FeaturesConfig.class, ReactorConfig.class})
 @ImportResource("WEB-INF/conf/mq-context.xml")
 public class ApplicationConfig {
@@ -109,12 +109,12 @@ public class ApplicationConfig {
 
     @Bean
     public XnatUserProvider primaryAdminUserProvider(final SiteConfigPreferences preferences) throws SiteConfigurationException {
-        return new XnatUserProvider(preferences.getPrimaryAdminUsername());
+        return new XnatUserProvider(preferences, "primaryAdminUsername");
     }
 
     @Bean
     public XnatUserProvider receivedFileUserProvider(final SiteConfigPreferences preferences) throws SiteConfigurationException {
-        return new XnatUserProvider(preferences.getReceivedFileUser());
+        return new XnatUserProvider(preferences, "receivedFileUser");
     }
 
     @Bean
diff --git a/src/main/java/org/nrg/xnat/configuration/NotificationsConfig.java b/src/main/java/org/nrg/xnat/configuration/NotificationsConfig.java
index 188e2c9878f997848ee33dd8ccc26b44b316e9f5..9b98c513ad4482a0408200c11fdf07920f387bda 100644
--- a/src/main/java/org/nrg/xnat/configuration/NotificationsConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/NotificationsConfig.java
@@ -18,6 +18,7 @@ import org.nrg.notify.renderers.ChannelRenderer;
 import org.nrg.notify.renderers.NrgMailChannelRenderer;
 import org.nrg.xdat.preferences.NotificationsPreferences;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
+import org.nrg.xdat.preferences.SmtpServer;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
@@ -27,7 +28,6 @@ import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Properties;
 
 @Configuration
 @ComponentScan({"org.nrg.mail.services", "org.nrg.notify.services.impl", "org.nrg.notify.daos"})
@@ -35,20 +35,16 @@ public class NotificationsConfig {
 
     @Bean
     public JavaMailSenderImpl mailSender(final NotificationsPreferences preferences) throws IOException, SiteConfigurationException {
-        final Map<String, String> smtp = preferences.getSmtpServer();
+        final SmtpServer         smtp   = preferences.getSmtpServer();
         final JavaMailSenderImpl sender = new JavaMailSenderImpl();
         if(smtp!=null) {
-            sender.setHost(StringUtils.defaultIfBlank(smtp.remove("host"), "localhost"));
-            sender.setPort(Integer.parseInt(StringUtils.defaultIfBlank(smtp.remove("port"), "25")));
-            sender.setUsername(StringUtils.defaultIfBlank(smtp.remove("username"), ""));
-            sender.setPassword(StringUtils.defaultIfBlank(smtp.remove("password"), ""));
-            sender.setProtocol(StringUtils.defaultIfBlank(smtp.remove("protocol"), "smtp"));
-            if (smtp.size() > 0) {
-                final Properties properties = new Properties();
-                for (final String property : smtp.keySet()) {
-                    properties.setProperty(property, smtp.get(property));
-                }
-                sender.setJavaMailProperties(properties);
+            sender.setHost(StringUtils.defaultIfBlank(smtp.getHostname(), "localhost"));
+            sender.setPort(smtp.getPort());
+            sender.setUsername(StringUtils.defaultIfBlank(smtp.getUsername(), ""));
+            sender.setPassword(StringUtils.defaultIfBlank(smtp.getPassword(), ""));
+            sender.setProtocol(StringUtils.defaultIfBlank(smtp.getProtocol(), "smtp"));
+            if (smtp.getMailProperties().size() > 0) {
+                sender.setJavaMailProperties(smtp.getMailProperties());
             }
         }
         return sender;
diff --git a/src/main/java/org/nrg/xnat/configuration/PreferencesConfig.java b/src/main/java/org/nrg/xnat/configuration/PreferencesConfig.java
index 2c6e157899bcbd7fbd049cfec81eaaf20b45836a..8262729f829ec5f21d5dd7083e311c5bdf0b2235 100644
--- a/src/main/java/org/nrg/xnat/configuration/PreferencesConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/PreferencesConfig.java
@@ -9,26 +9,19 @@
 
 package org.nrg.xnat.configuration;
 
-import org.nrg.config.exceptions.SiteConfigurationException;
 import org.nrg.config.services.ConfigService;
-import org.nrg.config.services.SiteConfigurationService;
 import org.nrg.config.services.UserConfigurationService;
 import org.nrg.config.services.impl.DefaultConfigService;
 import org.nrg.config.services.impl.DefaultUserConfigurationService;
 import org.nrg.prefs.configuration.NrgPrefsConfiguration;
-import org.nrg.prefs.services.NrgPreferenceService;
 import org.nrg.prefs.services.PreferenceService;
-import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xnat.resolvers.XnatPreferenceEntityResolver;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
-import org.springframework.core.env.Environment;
 
 import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 
 @Configuration
 @ComponentScan("org.nrg.config.daos")
@@ -45,26 +38,6 @@ public class PreferencesConfig {
         return new DefaultUserConfigurationService(configService());
     }
 
-    @Bean
-    public SiteConfigurationService siteConfigurationService(final SiteConfigPreferences preferences, final NrgPreferenceService nrgPreferenceService, final ConfigService configService, final Environment environment) throws ClassNotFoundException, IllegalAccessException, InstantiationException, SiteConfigurationException {
-        final Class<? extends SiteConfigurationService> clazz = Class.forName(preferences.getSiteConfigurationService()).asSubclass(SiteConfigurationService.class);
-        try {
-            @SuppressWarnings("unchecked")
-            final Constructor<? extends SiteConfigurationService> constructor = clazz.getConstructor(NrgPreferenceService.class, Environment.class);
-            return constructor.newInstance(nrgPreferenceService, environment);
-        } catch (NoSuchMethodException | InvocationTargetException ignored) {
-            // No worries, it just doesn't have that constructor.
-        }
-        try {
-            @SuppressWarnings("unchecked")
-            final Constructor<? extends SiteConfigurationService> constructor = clazz.getConstructor(ConfigService.class, Environment.class);
-            return constructor.newInstance(configService, environment);
-        } catch (NoSuchMethodException | InvocationTargetException ignored) {
-            // No worries, it just doesn't have that constructor.
-        }
-        return clazz.newInstance();
-    }
-
     @Bean
     public XnatPreferenceEntityResolver defaultResolver(final PreferenceService preferenceService) throws IOException {
         return new XnatPreferenceEntityResolver(preferenceService);
diff --git a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
index d867e03f2066aa53eaf10b398a51cb16d319aa06..21a34ed92c9ad77fd5728451369faaa406af0fe4 100644
--- a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
@@ -71,12 +71,12 @@ public class SchedulerConfig implements SchedulingConfigurer {
         _service.triggerEvent(new PreferenceEvent("inactivityBeforeLockout", String.valueOf(_siteConfigPreferences.getInactivityBeforeLockout())));
         _service.triggerEvent(new PreferenceEvent("maxFailedLoginsLockoutDuration", String.valueOf(_siteConfigPreferences.getMaxFailedLoginsLockoutDuration())));
         _service.triggerEvent(new PreferenceEvent("emailPrefix", String.valueOf(_notificationsPreferences.getEmailPrefix())));
-        _service.triggerEvent(new PreferenceEvent("host", String.valueOf(_notificationsPreferences.getHostname())));
+        _service.triggerEvent(new PreferenceEvent("smtpHostname", String.valueOf(_notificationsPreferences.getSmtpHostname())));
         _service.triggerEvent(new PreferenceEvent("requireLogin", String.valueOf(_siteConfigPreferences.getRequireLogin())));
-        _service.triggerEvent(new PreferenceEvent("security.channel", String.valueOf(_siteConfigPreferences.getSecurityChannel())));
+        _service.triggerEvent(new PreferenceEvent("securityChannel", String.valueOf(_siteConfigPreferences.getSecurityChannel())));
         _service.triggerEvent(new PreferenceEvent("passwordExpirationType", String.valueOf(_siteConfigPreferences.getPasswordExpirationType())));
         _service.triggerEvent(new PreferenceEvent("archivePath", String.valueOf(_siteConfigPreferences.getArchivePath())));
-        _service.triggerEvent(new PreferenceEvent("security.services.role.default", String.valueOf(_siteConfigPreferences.getRoleService())));
+        _service.triggerEvent(new PreferenceEvent("roleService", String.valueOf(_siteConfigPreferences.getRoleService())));
         _service.triggerEvent(new PreferenceEvent("checksums", String.valueOf(_siteConfigPreferences.getChecksums())));
         _service.triggerEvent(new PreferenceEvent("sitewidePetTracers", String.valueOf(_siteConfigPreferences.getSitewidePetTracers())));
 
diff --git a/src/main/java/org/nrg/xnat/configuration/XnatServicesConfig.java b/src/main/java/org/nrg/xnat/configuration/XnatServicesConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..18ae83c888af031c5e3012bdea2b58adfb202e4d
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/configuration/XnatServicesConfig.java
@@ -0,0 +1,21 @@
+/*
+ * web: org.nrg.xnat.configuration.XnatServicesConfig
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.configuration;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * This configuration manages configuration and instantiation of core XNAT/XDAT/XFT services.
+ */
+@Configuration
+@ComponentScan({"org.nrg.xnat.services.archive.impl", "org.nrg.xnat.services.system.impl.hibernate"})
+public class XnatServicesConfig {
+}
diff --git a/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java b/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
index 6e29b30a2ce6807a764f9b02b7b54b7a75c3a4fc..3af8c555615ffb14eb4170a414ac32cd31a76c1f 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
@@ -9,8 +9,6 @@
 
 package org.nrg.xnat.event.listeners;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.nrg.prefs.events.AbstractPreferenceHandler;
 import org.nrg.prefs.events.PreferenceHandlerMethod;
 import org.nrg.xdat.preferences.PreferenceEvent;
@@ -24,35 +22,31 @@ import java.util.List;
 
 @Service
 public class SiteConfigPreferenceHandler extends AbstractPreferenceHandler<PreferenceEvent> {
-	private String _toolId=SiteConfigPreferences.SITE_CONFIG_TOOL_ID;
-	private final List<PreferenceHandlerMethod> _methods = new ArrayList<>();
-
-	@Inject
-	public SiteConfigPreferenceHandler(final EventBus eventBus){
-		super(eventBus);
-	}
-
-	@Override
-	public String getToolId() {
-		return _toolId;
-	}
-
-	@Override
-	public void setToolId(String toolId) {
-		_toolId=toolId;
-	}
-
-	@Override
-	public List<PreferenceHandlerMethod> getMethods(){
-		return _methods;
-	}
-
-	@Override
-	public void addMethod(PreferenceHandlerMethod method){
-		_methods.add(method);
-	}
-
-	private static final Log _log = LogFactory.getLog(SiteConfigPreferenceHandler.class);
-
-
+    private       String                        _toolId  = SiteConfigPreferences.SITE_CONFIG_TOOL_ID;
+    private final List<PreferenceHandlerMethod> _methods = new ArrayList<>();
+
+    @Inject
+    public SiteConfigPreferenceHandler(final EventBus eventBus) {
+        super(eventBus);
+    }
+
+    @Override
+    public String getToolId() {
+        return _toolId;
+    }
+
+    @Override
+    public void setToolId(String toolId) {
+        _toolId = toolId;
+    }
+
+    @Override
+    public List<PreferenceHandlerMethod> getMethods() {
+        return _methods;
+    }
+
+    @Override
+    public void addMethod(PreferenceHandlerMethod method) {
+        _methods.add(method);
+    }
 }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractNotificationsPreferenceHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractNotificationsPreferenceHandlerMethod.java
index 1f65583a5895a87f3b622f6f7e4f6fe776233780..3857d280833fea45f976863e9d03dbb1581b3415 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractNotificationsPreferenceHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractNotificationsPreferenceHandlerMethod.java
@@ -9,14 +9,14 @@
 
 package org.nrg.xnat.event.listeners.methods;
 
-import org.nrg.prefs.events.PreferenceHandlerMethod;
+import org.nrg.prefs.events.AbstractPreferenceHandlerMethod;
 import org.nrg.xdat.preferences.NotificationsPreferences;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-public abstract class AbstractNotificationsPreferenceHandlerMethod implements PreferenceHandlerMethod {
+public abstract class AbstractNotificationsPreferenceHandlerMethod extends AbstractPreferenceHandlerMethod {
     @Override
     public List<String> getToolIds() {
         return new ArrayList<>(Collections.singletonList(NotificationsPreferences.NOTIFICATIONS_TOOL_ID));
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigNotificationsPreferenceHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigNotificationsPreferenceHandlerMethod.java
index 70c8f09450578dbb24f11571e8e5bee1b742f7fa..287e4f88a14b7b5e9fe0957b865757720c4125d1 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigNotificationsPreferenceHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigNotificationsPreferenceHandlerMethod.java
@@ -9,7 +9,7 @@
 
 package org.nrg.xnat.event.listeners.methods;
 
-import org.nrg.prefs.events.PreferenceHandlerMethod;
+import org.nrg.prefs.events.AbstractPreferenceHandlerMethod;
 import org.nrg.xdat.preferences.NotificationsPreferences;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 
@@ -17,7 +17,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-public abstract class AbstractSiteConfigNotificationsPreferenceHandlerMethod implements PreferenceHandlerMethod {
+public abstract class AbstractSiteConfigNotificationsPreferenceHandlerMethod extends AbstractPreferenceHandlerMethod {
     @Override
     public List<String> getToolIds() {
         return new ArrayList<>(Arrays.asList(NotificationsPreferences.NOTIFICATIONS_TOOL_ID, SiteConfigPreferences.SITE_CONFIG_TOOL_ID));
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigPreferenceHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigPreferenceHandlerMethod.java
index 6505be9f2d0000025dcae4efb4270039a82a01d4..bff16159aae74cc0d5a97d1016e3967a3a48020c 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigPreferenceHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/AbstractSiteConfigPreferenceHandlerMethod.java
@@ -9,7 +9,7 @@
 
 package org.nrg.xnat.event.listeners.methods;
 
-import org.nrg.prefs.events.PreferenceHandlerMethod;
+import org.nrg.prefs.events.AbstractPreferenceHandlerMethod;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xft.security.UserI;
 import org.nrg.xnat.utils.XnatUserProvider;
@@ -18,7 +18,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-public abstract class AbstractSiteConfigPreferenceHandlerMethod implements PreferenceHandlerMethod {
+public abstract class AbstractSiteConfigPreferenceHandlerMethod extends AbstractPreferenceHandlerMethod {
     protected AbstractSiteConfigPreferenceHandlerMethod() {
         _userProvider = null;
     }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/FeatureServicesHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/FeatureServicesHandlerMethod.java
index a3f8a0913902a48180013ebbfcb5ce66578c48ae..4e682c4820850895521381ae0443d3d17b45b6c0 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/FeatureServicesHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/FeatureServicesHandlerMethod.java
@@ -11,8 +11,6 @@ package org.nrg.xnat.event.listeners.methods;
 
 import com.google.common.collect.ImmutableList;
 import org.nrg.xdat.security.helpers.Features;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import java.util.Arrays;
@@ -46,7 +44,6 @@ public class FeatureServicesHandlerMethod extends AbstractSiteConfigPreferenceHa
         Features.setFeatureRepositoryServiceToSiteConfigPreference();
     }
 
-    private static final Logger       _log        = LoggerFactory.getLogger(FeatureServicesHandlerMethod.class);
-    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("security.services.feature.default", "security.services.featureRepository.default"));
+    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("featureService", "featureRepositoryService"));
 
 }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/InitializedHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/InitializedHandlerMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea96b98c6e4d6c8c0ba66599f5e04e164c89d4ee
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/InitializedHandlerMethod.java
@@ -0,0 +1,106 @@
+/*
+ * web: org.nrg.xnat.event.listeners.methods.RequiredChannelHandlerMethod
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.event.listeners.methods;
+
+import com.google.common.collect.ImmutableList;
+import org.nrg.framework.exceptions.NrgServiceError;
+import org.nrg.framework.exceptions.NrgServiceRuntimeException;
+import org.nrg.xapi.exceptions.InitializationException;
+import org.nrg.xdat.preferences.SiteConfigPreferences;
+import org.nrg.xnat.turbine.utils.ArcSpecManager;
+import org.nrg.xnat.utils.XnatUserProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class InitializedHandlerMethod extends AbstractSiteConfigPreferenceHandlerMethod {
+    @Autowired
+    public InitializedHandlerMethod(final SiteConfigPreferences preferences, final XnatUserProvider primaryAdminUserProvider) {
+        super(primaryAdminUserProvider);
+        _preferences = preferences;
+    }
+
+    @Override
+    public List<String> getHandledPreferences() {
+        return PREFERENCES;
+    }
+
+    @Override
+    public void handlePreferences(final Map<String, String> values) {
+        if (values.containsKey(INITIALIZED)) {
+            handlePreference(INITIALIZED, values.get(INITIALIZED));
+        }
+    }
+
+    @Override
+    public void handlePreference(final String preference, final String value) {
+        if (PREFERENCES.contains(preference)) {
+            try {
+                initialize();
+            } catch (InitializationException e) {
+                throw new NrgServiceRuntimeException(NrgServiceError.Unknown, "An error occurred attempting to complete system initialization", e);
+            }
+        }
+    }
+
+    private void initialize() throws InitializationException {
+        final String adminEmail = _preferences.getAdminEmail();
+        final String archivePath = _preferences.getArchivePath();
+        final String buildPath = _preferences.getBuildPath();
+        final String cachePath = _preferences.getCachePath();
+        final boolean enableCsrfToken = _preferences.getEnableCsrfToken();
+        final String ftpPath = _preferences.getFtpPath();
+        final String pipelinePath = _preferences.getPipelinePath();
+        final String prearchivePath = _preferences.getPrearchivePath();
+        final boolean requireLogin = _preferences.getRequireLogin();
+        final String siteId = _preferences.getSiteId();
+        final String siteUrl = _preferences.getSiteUrl();
+        final boolean userRegistration = _preferences.getUserRegistration();
+
+        // TODO: We may actually need to put a null check in here and make this a Future that circles back once everything is properly initialized.
+        final StringBuilder buffer = new StringBuilder("Preparing to complete system initialization with the final property settings of:").append(System.lineSeparator());
+        buffer.append(" * adminEmail: ").append(adminEmail).append(System.lineSeparator());
+        buffer.append(" * archivePath: ").append(archivePath).append(System.lineSeparator());
+        buffer.append(" * buildPath: ").append(buildPath).append(System.lineSeparator());
+        buffer.append(" * cachePath: ").append(cachePath).append(System.lineSeparator());
+        buffer.append(" * enableCsrfToken: ").append(enableCsrfToken).append(System.lineSeparator());
+        buffer.append(" * ftpPath: ").append(ftpPath).append(System.lineSeparator());
+        buffer.append(" * pipelinePath: ").append(pipelinePath).append(System.lineSeparator());
+        buffer.append(" * prearchivePath: ").append(prearchivePath).append(System.lineSeparator());
+        buffer.append(" * requireLogin: ").append(requireLogin).append(System.lineSeparator());
+        buffer.append(" * siteId: ").append(siteId).append(System.lineSeparator());
+        buffer.append(" * siteUrl: ").append(siteUrl).append(System.lineSeparator());
+        buffer.append(" * userRegistration: ").append(userRegistration).append(System.lineSeparator());
+
+        _log.info(buffer.toString());
+
+        // In the case where the application hasn't yet been initialized, this operation should mean that the system is
+        // being initialized from the set-up page. In that case, we need to propagate a few properties to the arc-spec
+        // persistence to support
+        try {
+            ArcSpecManager.initialize(getAdminUser());
+        } catch (Exception e) {
+            throw new InitializationException(e);
+        }
+    }
+
+    private static final Logger _log = LoggerFactory.getLogger(InitializedHandlerMethod.class);
+
+    private static final String       INITIALIZED = "initialized";
+    private static final List<String> PREFERENCES = ImmutableList.copyOf(Collections.singletonList(INITIALIZED));
+
+    private final SiteConfigPreferences _preferences;
+}
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/RequiredChannelHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/RequiredChannelHandlerMethod.java
index 87950b798488e691267e0f97acdebc9b58b4ad98..287d41d7f2e46d7c1d7d2e0d35e1ef103c5501fc 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/RequiredChannelHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/RequiredChannelHandlerMethod.java
@@ -50,7 +50,7 @@ public class RequiredChannelHandlerMethod extends AbstractSiteConfigPreferenceHa
         _filter.setRequiredChannel(_preferences.getSecurityChannel());
     }
 
-    private static final List<String> PREFERENCES = ImmutableList.copyOf(Collections.singletonList("security.channel"));
+    private static final List<String> PREFERENCES = ImmutableList.copyOf(Collections.singletonList("securityChannel"));
 
     private final SiteConfigPreferences              _preferences;
     private final TranslatingChannelProcessingFilter _filter;
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/RoleServicesHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/RoleServicesHandlerMethod.java
index 2299b72fd24f19647dcc6dd2e7dff6852a432894..0c503241ca87e1c0be17ba11f45f5b80db6bfa99 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/RoleServicesHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/RoleServicesHandlerMethod.java
@@ -77,7 +77,7 @@ public class RoleServicesHandlerMethod extends AbstractSiteConfigPreferenceHandl
     }
 
     private static final Logger       _log        = LoggerFactory.getLogger(RoleServicesHandlerMethod.class);
-    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("security.services.role.default", "security.services.roleRepository.default"));
+    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("roleService", "roleRepositoryService"));
 
     private final SiteConfigPreferences _preferences;
     private final RoleHolder            _roleHolder;
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/SmtpHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/SmtpHandlerMethod.java
index 296967f00b7f30eba2d6b18ccc69596b8c78e9c9..5dd7f1672badd850be41f08cae47e9813b48e431 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/methods/SmtpHandlerMethod.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/SmtpHandlerMethod.java
@@ -10,20 +10,23 @@
 package org.nrg.xnat.event.listeners.methods;
 
 import com.google.common.collect.ImmutableList;
-import org.nrg.mail.services.MailService;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang3.StringUtils;
+import org.nrg.mail.services.impl.SpringBasedMailServiceImpl;
 import org.nrg.xdat.preferences.NotificationsPreferences;
+import org.nrg.xdat.preferences.SmtpServer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.mail.javamail.JavaMailSenderImpl;
 import org.springframework.stereotype.Component;
 
 import java.util.*;
+import java.util.regex.Pattern;
 
 @Component
 public class SmtpHandlerMethod extends AbstractNotificationsPreferenceHandlerMethod {
     @Autowired
-    public SmtpHandlerMethod(final NotificationsPreferences preferences, final JavaMailSenderImpl mailSender, final MailService mailService) {
+    public SmtpHandlerMethod(final NotificationsPreferences preferences, final SpringBasedMailServiceImpl mailService) {
         _preferences = preferences;
-        this._mailSender = mailSender;
         _mailService = mailService;
     }
 
@@ -32,50 +35,77 @@ public class SmtpHandlerMethod extends AbstractNotificationsPreferenceHandlerMet
         return PREFERENCES;
     }
 
+    /**
+     * This implementation overrides the default version of this method to perform regular expression comparisons
+     * against the submitted preference values.
+     */
     @Override
-    public void handlePreferences(final Map<String, String> values) {
-        if (!Collections.disjoint(PREFERENCES, values.keySet())) {
-            updateSmtp();
+    public Set<String> findHandledPreferences(final Collection<String> preferences) {
+        final Set<String> handled = Sets.newHashSet();
+        for (final Pattern pattern : PREFS_PATTERNS) {
+            for (final String preference : preferences) {
+                if (pattern.matcher(preference).matches()) {
+                    handled.add(preference);
+                }
+            }
         }
+        return handled;
     }
 
     @Override
-    public void handlePreference(final String preference, final String value) {
-        if (PREFERENCES.contains(preference)) {
-            updateSmtp();
-        }
+    public void handlePreferences(final Map<String, String> values) {
+        updateSmtp(values);
     }
 
-    private void updateSmtp() {
-        final Properties oldMailProperties = _mailSender.getJavaMailProperties();
-
-        final boolean smtpEnabled = _preferences.getSmtpEnabled();
-        final boolean smtpAuth = _preferences.getSmtpAuth();
-        final boolean startTls = _preferences.getSmtpStartTls();
-        final String sslTrust = _preferences.getSmtpSSLTrust();
-
-        _mailSender.setHost(_preferences.getHostname());
-        _mailSender.setPort(_preferences.getPort());
-        _mailSender.setUsername(_preferences.getUsername());
-        _mailSender.setPassword(_preferences.getPassword());
-        _mailSender.setProtocol(_preferences.getProtocol());
-        _mailService.setSmtpEnabled(smtpEnabled);
+    @Override
+    public void handlePreference(final String preference, final String value) {
+        updateSmtp(Collections.singletonMap(preference, value));
+    }
 
-        oldMailProperties.setProperty("smtp.enabled", String.valueOf(smtpEnabled));
-        oldMailProperties.setProperty("mail.smtp.auth", String.valueOf(smtpAuth));
-        oldMailProperties.setProperty("mail.smtp.starttls.enable", String.valueOf(startTls));
+    private void updateSmtp(final Map<String, String> values) {
+        _mailService.setSmtpEnabled(_preferences.getSmtpEnabled());
+
+        final JavaMailSenderImpl mailSender = (JavaMailSenderImpl) _mailService.getJavaMailSender();
+        final SmtpServer         smtpServer = _preferences.getSmtpServer();
+
+        mailSender.setHost(smtpServer.getHostname());
+        mailSender.setPort(smtpServer.getPort());
+        mailSender.setUsername(smtpServer.getUsername());
+        mailSender.setPassword(smtpServer.getPassword());
+        mailSender.setProtocol(smtpServer.getProtocol());
+
+        final Properties properties = mailSender.getJavaMailProperties();
+
+        if (!smtpServer.getSmtpAuth()) {
+            properties.remove(SmtpServer.SMTP_KEY_AUTH);
+            properties.remove(SmtpServer.SMTP_KEY_STARTTLS_ENABLE);
+            properties.remove(SmtpServer.SMTP_KEY_SSL_TRUST);
+        } else {
+            properties.setProperty(SmtpServer.SMTP_KEY_AUTH, "true");
+            properties.setProperty(SmtpServer.SMTP_KEY_STARTTLS_ENABLE, Boolean.toString(smtpServer.getSmtpStartTls()));
+            if (StringUtils.isNotBlank(smtpServer.getSmtpSslTrust())) {
+                properties.setProperty(SmtpServer.SMTP_KEY_SSL_TRUST, smtpServer.getSmtpSslTrust());
+            }
+            properties.putAll(smtpServer.getMailProperties());
+        }
 
-        if (sslTrust != null) {
-            oldMailProperties.setProperty("mail.smtp.ssl.trust", sslTrust);
+        for (final String property : values.keySet()) {
+            if (!PREFERENCES.contains(property)) {
+                final String value = values.get(property);
+                if (StringUtils.isBlank(value) && properties.containsKey(value)) {
+                    properties.remove(value);
+                } else {
+                    properties.setProperty(property, value);
+                }
+            }
         }
 
-        _mailSender.setJavaMailProperties(oldMailProperties);
+        mailSender.setJavaMailProperties(properties);
     }
 
-    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("smtp.enabled", "host", "port", "username", "password", "protocol", "smtp.enabled", "mail.smtp.auth", "mail.smtp.starttls.enable", "mail.smtp.ssl.trust"));
-
-    private final NotificationsPreferences _preferences;
-    private final JavaMailSenderImpl       _mailSender;
-    private final MailService              _mailService;
+    private static final List<String>  PREFERENCES    = ImmutableList.copyOf(Arrays.asList("smtpHostname", "smtpPort", "smtpUsername", "smtpPassword", "smtpProtocol", "smtpAuth", "smtpStartTls", "smtpSslTrust"));
+    private static final List<Pattern> PREFS_PATTERNS = ImmutableList.copyOf(Collections.singletonList(Pattern.compile("^smtp[A-Z].*$")));
 
+    private final NotificationsPreferences   _preferences;
+    private final SpringBasedMailServiceImpl _mailService;
 }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/XnatUserProviderPreferenceHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/XnatUserProviderPreferenceHandlerMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..f41ce838a76f80586000acfeb7ba3d8afb4e353b
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/XnatUserProviderPreferenceHandlerMethod.java
@@ -0,0 +1,54 @@
+/*
+ * web: org.nrg.xnat.event.listeners.methods.ReceivedFileUserPreferenceHandlerMethod
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.event.listeners.methods;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
+import org.nrg.xnat.utils.XnatUserProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class XnatUserProviderPreferenceHandlerMethod extends AbstractSiteConfigPreferenceHandlerMethod {
+    @Autowired
+    public XnatUserProviderPreferenceHandlerMethod(final List<XnatUserProvider> providers) {
+        for (final XnatUserProvider provider : providers) {
+            _providers.put(provider.getUserKey(), provider);
+        }
+    }
+
+    @Override
+    public List<String> getHandledPreferences() {
+        return new ArrayList<>(_providers.keySet());
+    }
+
+    @Override
+    public void handlePreferences(final Map<String, String> values) {
+        for (final String key : values.keySet()) {
+            handlePreference(key, values.get(key));
+        }
+    }
+
+    @Override
+    public void handlePreference(final String preference, final String value) {
+        if (_providers.containsKey(preference)) {
+            final XnatUserProvider provider = _providers.get(preference);
+            if (!StringUtils.equals(value, provider.getLogin())) {
+                provider.setLogin(value);
+            }
+        }
+    }
+
+    private final Map<String, XnatUserProvider> _providers = Maps.newHashMap();
+}
diff --git a/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java b/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java
index dd82e99f24ebf2428c5ac12c894475166e752c73..2850af58f7279873a5dbe325ca857ca125c28088 100644
--- a/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java
+++ b/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java
@@ -15,6 +15,7 @@ import org.apache.commons.io.filefilter.DirectoryFileFilter;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.config.entities.Configuration;
 import org.nrg.framework.constants.Scope;
+import org.nrg.xapi.exceptions.InsufficientPrivilegesException;
 import org.nrg.xdat.XDAT;
 import org.nrg.xdat.model.ArcProjectI;
 import org.nrg.xdat.om.ArcProject;
@@ -223,12 +224,11 @@ public class PrearcUtils {
     public static File getPrearcDir(final UserI user, final String project, final boolean allowUnassigned) throws Exception {
         String prearcPath;
         String prearchRootPref = XDAT.getSiteConfigPreferences().getPrearchivePath();
-        String prearchRootArcSpec = ArcSpecManager.GetInstance().getGlobalPrearchivePath();
         if (project == null || project.equals(COMMON)) {
             if (allowUnassigned || user == null || Roles.isSiteAdmin(user)) {
                 prearcPath = prearchRootPref;
             } else {
-                throw new InvalidPermissionException("user " + user.getUsername() + " does not have permission to access the Unassigned directory ");
+                throw new InsufficientPrivilegesException(user.getUsername());
             }
         } else {
             //Refactored to remove unnecessary database hits.  It only needs to hit the xnat_projectdata table if the query is using a project alias rather than a project id.  TO
@@ -306,8 +306,8 @@ public class PrearcUtils {
      * @param project If the project is null, it is the unassigned project
      *                project abbreviation or alias
      * @return true if the user has permissions to access the project, false otherwise
-     * @throws Exception
-     * @throws IOException
+     * @throws Exception When something goes wrong.
+     * @throws IOException When an error occurs reading or writing data.
      */
     @SuppressWarnings("unused")
     public static boolean validUser(final UserI user, final String project, final boolean allowUnassigned) throws Exception {
@@ -525,7 +525,7 @@ public class PrearcUtils {
     }
 
     public static String makeUri(final String urlBase, final String timestamp, final String folderName) {
-        return StringUtils.join(new String[]{urlBase, "/", timestamp, "/", folderName});
+        return StringUtils.join(urlBase, "/", timestamp, "/", folderName);
     }
 
     public static Map<String, Object> parseURI(final String uri) throws MalformedURLException {
@@ -533,7 +533,7 @@ public class PrearcUtils {
     }
 
     public static String buildURI(final String project, final String timestamp, final String folderName) {
-        return StringUtils.join(new String[]{"/prearchive/projects/", (project == null) ? PrearcUtils.COMMON : project, "/", timestamp, "/", folderName});
+        return StringUtils.join("/prearchive/projects/", (project == null) ? PrearcUtils.COMMON : project, "/", timestamp, "/", folderName);
     }
 
     public static XFTTable convertArrayLtoTable(ArrayList<ArrayList<Object>> rows) {
@@ -809,8 +809,8 @@ public class PrearcUtils {
      * @param session  The session to be locked.
      * @param filename The filename to be locked.
      * @return PrearcFileLock
-     * @throws SessionFileLockException
-     * @throws IOException
+     * @throws SessionFileLockException When an attempt is made to access a locked file.
+     * @throws IOException When an error occurs reading or writing data.
      */
     public static PrearcFileLock lockFile(final SessionDataTriple session, final String filename) throws SessionFileLockException, IOException {
         //putting these in a subdirectory of the cache space
@@ -858,19 +858,26 @@ public class PrearcUtils {
 
         synchronized (syncLock) {
             //synchronized to prevent overlap with .lockFile()
-            if (name.exists() && (name.list() == null || name.list().length == 0)) {
-                try {
-                    FileUtils.deleteDirectory(name);
-
-                    if (timestamp.exists() && (timestamp.list() == null || timestamp.list().length == 0)) {
-                        FileUtils.deleteDirectory(timestamp);
-
-                        if (project.exists() && (project.list() == null || project.list().length == 0)) {
-                            FileUtils.deleteDirectory(project);
+            if (name.exists()) {
+                final String[] names = name.list();
+                if (names == null || names.length == 0) {
+                    try {
+                        FileUtils.deleteDirectory(name);
+                        if (timestamp.exists()) {
+                            final String[] timestamps = timestamp.list();
+                            if (timestamps == null || timestamps.length == 0) {
+                                FileUtils.deleteDirectory(timestamp);
+                                if (project.exists()) {
+                                    final String[] projects = project.list();
+                                    if (projects == null || projects.length == 0) {
+                                        FileUtils.deleteDirectory(project);
+                                    }
+                                }
+                            }
                         }
+                    } catch (IOException e) {
+                        logger.error("Couldn't clean temporary lock directories in the cache folder.", e);
                     }
-                } catch (IOException e) {
-                    logger.error("Couldn't clean temporary lock directories in the cache folder.", e);
                 }
             }
         }
diff --git a/src/main/java/org/nrg/xnat/initialization/InitializingTasksExecutor.java b/src/main/java/org/nrg/xnat/initialization/InitializingTasksExecutor.java
index c43a3490a8f6d182bbae0b4bdc38afa9f728c258..eef2cdd586ba250295e854b13f3e51304032b587 100644
--- a/src/main/java/org/nrg/xnat/initialization/InitializingTasksExecutor.java
+++ b/src/main/java/org/nrg/xnat/initialization/InitializingTasksExecutor.java
@@ -37,9 +37,7 @@ public class InitializingTasksExecutor {
 
     @EventListener
     public void executeOnContextRefresh(final ContextRefreshedEvent event) {
-        if (_log.isDebugEnabled()) {
-            _log.debug("Handling context refreshed event at " + event.getTimestamp());
-        }
+        _log.debug("Handling context refreshed event at {}", event.getTimestamp());
         if (_future == null || _future.isCancelled()) {
             _future = _scheduler.scheduleWithFixedDelay(new CheckTasks(), 15000);
         }
@@ -56,8 +54,10 @@ public class InitializingTasksExecutor {
                     }
                     try {
                         final boolean completed = task.call();
-                        if (_log.isInfoEnabled()) {
-                            _log.info("Task \"{}\" {}", task.getTaskName(), completed ? "completed at " + task.completedAt() : "did not complete.");
+                        if (completed) {
+                            _log.info("Task \"{}\" completed at {}", task.getTaskName(), task.completedAt());
+                        } else {
+                            _log.debug("Task \"{}\" not yet completed, {} executions attempted.", task.getTaskName(), task.executions());
                         }
                         results.put(task.getTaskName(), completed);
                     } catch (Exception e) {
diff --git a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
index 3546bb502d98f58de40fe0ef8e5cf132f3ccd6ab..2664660544cdf117f89d314545d1c100ad81895c 100644
--- a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
+++ b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
@@ -10,6 +10,7 @@
 package org.nrg.xnat.initialization;
 
 import org.nrg.config.exceptions.SiteConfigurationException;
+import org.nrg.framework.configuration.ConfigPaths;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xdat.services.AliasTokenService;
 import org.nrg.xdat.services.XdatUserAuthService;
@@ -155,13 +156,13 @@ public class SecurityConfig {
     }
 
     @Bean
-    public AuthenticationProviderAggregator providerAggregator(final List<AuthenticationProvider> providers, final List<AuthenticationProviderConfigurator> configurators) {
+    public AuthenticationProviderAggregator providerAggregator(final List<AuthenticationProvider> providers, final List<AuthenticationProviderConfigurator> configurators, final ConfigPaths configFolderPaths) {
         final Map<String, AuthenticationProviderConfigurator> configuratorMap = new HashMap<>();
         for (final AuthenticationProviderConfigurator configurator : configurators) {
             configuratorMap.put(configurator.getConfiguratorId(), configurator);
         }
 
-        return new AuthenticationProviderAggregator(providers, configuratorMap);
+        return new AuthenticationProviderAggregator(providers, configuratorMap, configFolderPaths);
     }
 
     @Bean
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/AbstractInitializingTask.java b/src/main/java/org/nrg/xnat/initialization/tasks/AbstractInitializingTask.java
index 5891777771d9db1de3d96c7c76388390bcf72208..abd84578408d83d35d2364fdc918cd07df8a5920 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/AbstractInitializingTask.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/AbstractInitializingTask.java
@@ -33,12 +33,11 @@ public abstract class AbstractInitializingTask implements InitializingTask {
             return true;
         } catch (InitializingTaskException e) {
             switch (e.getLevel()) {
+                case RequiresInitialization:
+                    manageSingleNotice("The system is not yet fully initialized. Delaying execution of the intializing task \"" + getTaskName() + "\" until initialization is completed.");
+                    break;
                 case SingleNotice:
-                    if (_executions == 1) {
-                        _log.info(e.getMessage());
-                    } else {
-                        _log.debug(e.getMessage());
-                    }
+                    manageSingleNotice(e.getMessage());
                     break;
                 case Info:
                     _log.info(e.getMessage());
@@ -93,6 +92,14 @@ public abstract class AbstractInitializingTask implements InitializingTask {
 
     protected abstract void callImpl() throws InitializingTaskException;
 
+    private void manageSingleNotice(final String message) {
+        if (_executions == 1) {
+            _log.info(message);
+        } else {
+            _log.debug(message);
+        }
+    }
+
     private static final Logger _log = LoggerFactory.getLogger(AbstractInitializingTask.class);
 
     private final int _maxExecutions;
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/CreateOrUpdateDatabaseViews.java b/src/main/java/org/nrg/xnat/initialization/tasks/CreateOrUpdateDatabaseViews.java
index 858d25cbdeeb975d84126802a4e5bc4d538a1c09..417baf736605c6bba9fef1f240a2997fab899fdd 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/CreateOrUpdateDatabaseViews.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/CreateOrUpdateDatabaseViews.java
@@ -14,7 +14,6 @@ import org.nrg.xdat.display.DisplayManager;
 import org.nrg.xdat.servlet.XDATServlet;
 import org.nrg.xft.db.PoolDBUtils;
 import org.nrg.xft.exception.DBPoolException;
-import org.nrg.xft.exception.XFTInitException;
 import org.nrg.xft.schema.XFTManager;
 import org.nrg.xnat.services.XnatAppInfo;
 import org.slf4j.Logger;
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/EncryptXnatPasswords.java b/src/main/java/org/nrg/xnat/initialization/tasks/EncryptXnatPasswords.java
index bc2657fa9d02339dc1b4490381abee8d51df45a0..60244b2e4d7c586c9e4f7b0307a70d96393a87fc 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/EncryptXnatPasswords.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/EncryptXnatPasswords.java
@@ -58,12 +58,12 @@ public class EncryptXnatPasswords extends AbstractInitializingTask {
                         _template.update("DELETE FROM xs_item_cache");
                     }
                 } catch (BadSqlGrammarException e) {
-                    throw new InitializingTaskException(InitializingTaskException.Level.Info, "Unable to execute user table password encryption. The table may not exist yet.", e);
+                    throw new InitializingTaskException(InitializingTaskException.Level.RequiresInitialization);
                 } catch (Exception e) {
                     throw new InitializingTaskException(InitializingTaskException.Level.Error, "An error occurred trying to access the database and update the user table password encryption.", e);
                 }
             } else {
-                throw new InitializingTaskException(InitializingTaskException.Level.SingleNotice, "The xdat_user table does not exist yet, deferring password encryption.");
+                throw new InitializingTaskException(InitializingTaskException.Level.RequiresInitialization);
             }
         } catch (SQLException e) {
             throw new InitializingTaskException(InitializingTaskException.Level.Error, "An SQL error occurred trying to test for the existence of the xdat_user table.", e);
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/GetSiteWidePETTracerList.java b/src/main/java/org/nrg/xnat/initialization/tasks/GetSiteWidePETTracerList.java
index 849a491193a2d0890a08b424cabed44e8be7ea43..16d54d5bf7e835da00e8e849f256eff1a4d58be0 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/GetSiteWidePETTracerList.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/GetSiteWidePETTracerList.java
@@ -44,7 +44,7 @@ public class GetSiteWidePETTracerList extends AbstractInitializingTask {
                 _petTracerUtils.setSiteWideTracerList(_adminUsername, path, siteWide);
             }
         } catch (ConfigServiceException e) {
-            throw new InitializingTaskException(InitializingTaskException.Level.Warn, "An error occurred access the configuration service, it may not be initialized yet.", e);
+            throw new InitializingTaskException(InitializingTaskException.Level.Warn, "An error occurred accessing the configuration service, it may not be initialized yet.", e);
         } catch (IOException e) {
             throw new InitializingTaskException(InitializingTaskException.Level.Error, "Unable to either find or retrieve the PET tracer list.", e);
         }
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/InitializingTaskException.java b/src/main/java/org/nrg/xnat/initialization/tasks/InitializingTaskException.java
index 59901dc2ac105e9591c0fd5a5387907c7d5c8b1b..c261880cfd3f22370c5506ac739ad1cb08431477 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/InitializingTaskException.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/InitializingTaskException.java
@@ -10,6 +10,10 @@
 package org.nrg.xnat.initialization.tasks;
 
 public class InitializingTaskException extends Exception {
+    public InitializingTaskException(final Level level) {
+        _level = level;
+    }
+
     public InitializingTaskException(final Level level, final String message) {
         super(message);
         _level = level;
@@ -25,6 +29,7 @@ public class InitializingTaskException extends Exception {
     }
 
     enum Level {
+        RequiresInitialization,
         SingleNotice,
         Info,
         Warn,
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/SystemPathVerification.java b/src/main/java/org/nrg/xnat/initialization/tasks/SystemPathVerification.java
index b189b45c2f382a5854ae96e682a788f8ad195b13..c8645c7c1b36419487b4dbd13d2c0a3d2a2f44ff 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/SystemPathVerification.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/SystemPathVerification.java
@@ -47,7 +47,7 @@ public class SystemPathVerification extends AbstractInitializingTask {
     protected void callImpl() throws InitializingTaskException {
         try {
             if (!_appInfo.isInitialized() || !_helper.tableExists("xnat_abstractresource") || !_helper.tableExists("xdat_user")) {
-                throw new InitializingTaskException(InitializingTaskException.Level.SingleNotice, "The system is not yet fully initialized. Delaying system path verification until initialization is completed.");
+                throw new InitializingTaskException(InitializingTaskException.Level.RequiresInitialization);
             }
         } catch (SQLException e) {
             throw new InitializingTaskException(InitializingTaskException.Level.SingleNotice, "An error occurred trying to check for the existence of the abstract resource table. This probably means the system is not yet fully initialized. Delaying system path verification until initialization is completed.");
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/UpdateNewSecureDefinitions.java b/src/main/java/org/nrg/xnat/initialization/tasks/UpdateNewSecureDefinitions.java
index ea98b8efda121ca20535438055476564cf5429a1..344d7f7721391ee1cb66e521bc1984db2b43b82d 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/UpdateNewSecureDefinitions.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/UpdateNewSecureDefinitions.java
@@ -37,7 +37,7 @@ public class UpdateNewSecureDefinitions extends AbstractInitializingTask {
                 _featureRepositoryService.updateNewSecureDefinitions();
             }
         } catch (Exception ignore) {
-            throw new InitializingTaskException(InitializingTaskException.Level.SingleNotice, "An error occurred retrieving element security settings. This usually just means they haven't yet been initialized.");
+            throw new InitializingTaskException(InitializingTaskException.Level.RequiresInitialization);
         }
     }
 
diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/UpdateUserAuthTable.java b/src/main/java/org/nrg/xnat/initialization/tasks/UpdateUserAuthTable.java
index d05381517607a3a4efe028698bb635239fbfc46e..dfaf3fdeaaae8c30054da705decf2cd1dae993d5 100644
--- a/src/main/java/org/nrg/xnat/initialization/tasks/UpdateUserAuthTable.java
+++ b/src/main/java/org/nrg/xnat/initialization/tasks/UpdateUserAuthTable.java
@@ -68,7 +68,7 @@ public class UpdateUserAuthTable extends AbstractInitializingTask {
             _log.debug("Updating the user auth table to set password updated to the current time for local users");
             _template.execute("UPDATE xhbm_xdat_user_auth SET password_updated=current_timestamp WHERE auth_method='" + XdatUserAuthService.LOCALDB + "' AND password_updated IS NULL");
         } catch (BadSqlGrammarException e) {
-            throw new InitializingTaskException(InitializingTaskException.Level.SingleNotice, "Unable to execute user auth table update, the table probably doesn't exist yet.", e);
+            throw new InitializingTaskException(InitializingTaskException.Level.RequiresInitialization);
         }
     }
 
diff --git a/src/main/java/org/nrg/xnat/restlet/actions/TriggerPipelines.java b/src/main/java/org/nrg/xnat/restlet/actions/TriggerPipelines.java
index e3570b3b257bf5b6352e0c0dd4977f489ce8f32b..a30abd942b135c57b84b489e62b32d6c93c3ad98 100644
--- a/src/main/java/org/nrg/xnat/restlet/actions/TriggerPipelines.java
+++ b/src/main/java/org/nrg/xnat/restlet/actions/TriggerPipelines.java
@@ -64,7 +64,7 @@ public class TriggerPipelines implements Callable<Boolean> {
         xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
         xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
         xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
-        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
         xnatPipelineLauncher.setParameter("sessionType", expt.getXSIType());
         xnatPipelineLauncher.setParameter("xnat_project", expt.getProject());
 
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/ProjtExptPipelineResource.java b/src/main/java/org/nrg/xnat/restlet/resources/ProjtExptPipelineResource.java
index a3cf5b56aa5aead85dff37aa8ff7c64a49b77d0d..d2d064b1c34d6b249192abca06343874556ad332 100644
--- a/src/main/java/org/nrg/xnat/restlet/resources/ProjtExptPipelineResource.java
+++ b/src/main/java/org/nrg/xnat/restlet/resources/ProjtExptPipelineResource.java
@@ -279,7 +279,7 @@ public class ProjtExptPipelineResource extends SecureResource {
         hasParams.add("userfullname");
         xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
         hasParams.add("adminemail");
-        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
         hasParams.add("mailhost");
         xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
         hasParams.add("xnatserver");
@@ -394,7 +394,7 @@ public class ProjtExptPipelineResource extends SecureResource {
         xnatPipelineLauncher.setParameter("useremail", user.getEmail());
 	    xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
 	    xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
-	    xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+	    xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
 	    xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
 
 
diff --git a/src/main/java/org/nrg/xnat/restlet/services/RefreshCatalog.java b/src/main/java/org/nrg/xnat/restlet/services/RefreshCatalog.java
index 7bb3b4ef458aa4f0db7263870490c0b52d6970f4..eb650577321b92c13cc0e6f91f8b94033cc0eb6e 100644
--- a/src/main/java/org/nrg/xnat/restlet/services/RefreshCatalog.java
+++ b/src/main/java/org/nrg/xnat/restlet/services/RefreshCatalog.java
@@ -14,14 +14,10 @@ import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import org.nrg.action.ActionException;
 import org.nrg.action.ClientException;
-import org.nrg.xft.event.EventUtils;
-import org.nrg.xnat.helpers.uri.URIManager;
-import org.nrg.xnat.helpers.uri.URIManager.ArchiveItemURI;
-import org.nrg.xnat.helpers.uri.UriParserUtils;
+import org.nrg.xdat.XDAT;
 import org.nrg.xnat.restlet.resources.SecureResource;
 import org.nrg.xnat.restlet.util.RequestUtil;
-import org.nrg.xnat.turbine.utils.ArchivableItem;
-import org.nrg.xnat.utils.ResourceUtils;
+import org.nrg.xnat.services.archive.CatalogService;
 import org.restlet.Context;
 import org.restlet.data.Form;
 import org.restlet.data.Request;
@@ -34,95 +30,100 @@ import java.util.List;
 
 public class RefreshCatalog extends SecureResource {
 
-	public RefreshCatalog(Context context, Request request, Response response) {
-		super(context, request, response);
-	}
-
-	@Override
-	public boolean allowGet() {
-		return true;
-	}
-
-	@Override
-	public boolean allowPost() {
-		return true;
-	}
-
-	List<String> resources=Lists.newArrayList();
-	ListMultimap<String,Object> otherParams=ArrayListMultimap.create();
-
-    private boolean populateStats, checksum, delete, append = false;
-	
-	public void handleParam(final String key,final Object value) throws ClientException{
-		if(value!=null){
-			if(key.equals("resource")){
-				resources.add((String)value);
-			}else if(key.equals("populateStats")){
-                populateStats=isQueryVariableTrueHelper(value);
-			}else if(key.equals("checksum")){
-				checksum=isQueryVariableTrueHelper(value);
-			}else if(key.equals("delete")){
-				delete=isQueryVariableTrueHelper(value);
-			}else if(key.equals("append")){
-				append=isQueryVariableTrueHelper(value);
-			}else if(key.equals("options")){
-				List<String> options=Arrays.asList(((String)value).split(","));
-				if(options.contains("populateStats")){
-					populateStats=true;
-				}
-				if(options.contains("checksum")){
-					checksum=true;
-				}
-				if(options.contains("delete")){
-					delete=true;
-				}
-				if(options.contains("append")){
-					append=true;
-				}
-			}else{
-				otherParams.put(key, value);
-			}
-		}
-	}
-
-	@Override
-	public void handlePost() {
-		try {
-			final Representation entity = this.getRequest().getEntity();
-
-			//parse body to identify resources if its multi-part form data
-			//TODO: Handle JSON body.
-			if (entity.isAvailable() && RequestUtil.isMultiPartFormData(entity)) {
-				loadParams(new Form(entity));
-			}
-			loadQueryVariables();//parse query string to identify resources
-
-			for(final String resource:resources){
-				//parse passed URI parameter
-				URIManager.DataURIA uri=UriParserUtils.parseURI(resource);
-
-				if(!(uri instanceof ArchiveItemURI)) {
-					throw new ClientException("Invalid Resource URI:"+ resource);
-				}
-
-                ArchiveItemURI resourceURI = (ArchiveItemURI) uri;
-
-                ArchivableItem existenceCheck = resourceURI.getSecurityItem();
-                if (existenceCheck != null) {
-                    //call refresh operation
-                    ResourceUtils.refreshResourceCatalog(resourceURI, getUser(), this.newEventInstance(EventUtils.CATEGORY.DATA, "Catalog(s) Refreshed"), populateStats, checksum, delete, append);
-                }
-			}
-
-			this.getResponse().setStatus(Status.SUCCESS_OK);
-		} catch (ActionException e) {
-			this.getResponse().setStatus(e.getStatus(), e.getMessage());
-			logger.error("",e);
-		} catch (Exception e) {
-			this.getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, e.getMessage());
-			logger.error("",e);
-		}
-	}
-
-
+    public RefreshCatalog(Context context, Request request, Response response) {
+        super(context, request, response);
+        _catalogService = XDAT.getContextService().getBean(CatalogService.class);
+    }
+
+    @Override
+    public boolean allowGet() {
+        return true;
+    }
+
+    @Override
+    public boolean allowPost() {
+        return true;
+    }
+
+    public void handleParam(final String key, final Object value) throws ClientException {
+        if (value != null) {
+            switch (key) {
+                case "resource":
+                    _resources.add((String) value);
+                    break;
+                case "append":
+                    if (isQueryVariableTrueHelper(value)) {
+                        _operations.add(CatalogService.Operation.Append);
+                    }
+                    break;
+                case "checksum":
+                    if (isQueryVariableTrueHelper(value)) {
+                        _operations.add(CatalogService.Operation.Checksum);
+                    }
+                    break;
+                case "delete":
+                    if (isQueryVariableTrueHelper(value)) {
+                        _operations.add(CatalogService.Operation.Delete);
+                    }
+                    break;
+                case "populateStats":
+                    if (isQueryVariableTrueHelper(value)) {
+                        _operations.add(CatalogService.Operation.PopulateStats);
+                    }
+                    break;
+                case "options":
+                    List<String> options = Arrays.asList(((String) value).split(","));
+                    if (options.contains("append")) {
+                        _operations.add(CatalogService.Operation.Append);
+                    }
+                    if (options.contains("checksum")) {
+                        _operations.add(CatalogService.Operation.Checksum);
+                    }
+                    if (options.contains("delete")) {
+                        _operations.add(CatalogService.Operation.Delete);
+                    }
+                    if (options.contains("populateStats")) {
+                        _operations.add(CatalogService.Operation.PopulateStats);
+                    }
+                    break;
+                default:
+                    _otherParams.put(key, value);
+                    break;
+            }
+        }
+        if (_operations.contains(CatalogService.Operation.All)) {
+            _operations.clear();
+            _operations.addAll(CatalogService.Operation.ALL);
+        }
+    }
+
+    @Override
+    public void handlePost() {
+        try {
+            final Representation entity = this.getRequest().getEntity();
+
+            //parse body to identify resources if its multi-part form data
+            //TODO: Handle JSON body.
+            if (entity.isAvailable() && RequestUtil.isMultiPartFormData(entity)) {
+                loadParams(new Form(entity));
+            }
+            loadQueryVariables();//parse query string to identify resources
+
+            _catalogService.refreshResourceCatalogs(getUser(), _resources, _operations.toArray(new CatalogService.Operation[_operations.size()]));
+
+            this.getResponse().setStatus(Status.SUCCESS_OK);
+        } catch (ActionException e) {
+            this.getResponse().setStatus(e.getStatus(), e.getMessage());
+            logger.error("", e);
+        } catch (Exception e) {
+            this.getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, e.getMessage());
+            logger.error("", e);
+        }
+    }
+
+    private final CatalogService _catalogService;
+
+    private final List<String>                   _resources   = Lists.newArrayList();
+    private final ListMultimap<String, Object>   _otherParams = ArrayListMultimap.create();
+    private final List<CatalogService.Operation> _operations  = Lists.newArrayList();
 }
diff --git a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java
index 73fe820b6b8bd9cc1b98465811834ef36906aa8a..33cc9d21fb03eaccce5a9bbb0b1f3e154ac05d01 100644
--- a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java
+++ b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java
@@ -9,7 +9,11 @@
 
 package org.nrg.xnat.security.config;
 
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.DirectoryFileFilter;
+import org.apache.commons.io.filefilter.RegexFileFilter;
 import org.apache.commons.lang3.StringUtils;
+import org.nrg.framework.configuration.ConfigPaths;
 import org.nrg.framework.utilities.BasicXnatResourceLocator;
 import org.nrg.xnat.security.provider.XnatAuthenticationProvider;
 import org.slf4j.Logger;
@@ -18,45 +22,108 @@ import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PropertiesLoaderUtils;
 import org.springframework.security.authentication.AuthenticationProvider;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.*;
 
 public class AuthenticationProviderAggregator extends ArrayList<AuthenticationProvider> {
 
-    public AuthenticationProviderAggregator(List<AuthenticationProvider> standaloneProviders, Map<String, AuthenticationProviderConfigurator> configurators) {
+    public AuthenticationProviderAggregator(List<AuthenticationProvider> standaloneProviders, Map<String, AuthenticationProviderConfigurator> configurators, final ConfigPaths configFolderPaths) {
         ArrayList<HashMap<String, String>> providerList = new ArrayList<>();
 
-        // Populate map of properties
+        // Populate map of properties for each provider
         try {
-            String               filenameEnd = "-provider.properties";
-            final List<Resource> resources   = BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/auth/**/*" + filenameEnd);
-            if(resources==null || resources.isEmpty()){
-                String            dbName        = "Database";
-                String            dbId          = "localdb";
-                String            dbType        = "db";
-                HashMap<String, String> dbProv = new HashMap<String, String>();
-                dbProv.put("name", dbName);
-                dbProv.put("id", dbId);
-                dbProv.put("type", dbType);
-                providerList.add(dbProv);
+            String filenameEnd = "-provider.properties";
+
+            List<Resource> resources = null;
+
+            ArrayList<String> authFilePaths = new ArrayList<>();
+            try {
+                //First see if there are any properties files in config/auth
+                for (Path currPath : configFolderPaths){
+                    Path authPath = Paths.get(currPath.toString(), "auth");
+
+                    _log.debug("AuthPath is " + authPath.toString());
+                    Collection<File> files = FileUtils.listFiles(
+                            authPath.toFile(),
+                            new RegexFileFilter("^.*" + filenameEnd),
+                            DirectoryFileFilter.DIRECTORY
+                    );
+                    if (files == null) {
+                        _log.debug("No files were found in " + authPath);
+                    } else {
+                        for (File file : files) {
+                            if (!authFilePaths.contains(file.toString())) {
+                                authFilePaths.add(file.toString());
+                            }
+                        }
+                    }
+                }
             }
-            else {
-                for (final Resource resource : resources) {
-                    String filename = resource.getFilename();
-                    String id = filename.substring(0, (filename.length() - filenameEnd.length()));
+            catch(Exception e){
+                _log.debug("Could not find auth properties files in config/auth.");
+            }
+            if(authFilePaths!=null || !authFilePaths.isEmpty()){
+                //If there were provider properties files in config/auth, use them to populate provider list
+                for (final String authFilePath : authFilePaths) {
+                    _log.debug("Accessing properties from "+authFilePath);
+                    Properties props = new Properties();
+                    InputStream inputStream = new FileInputStream(authFilePath);
+                    if(inputStream!=null){
+                        props.load(inputStream);
+                    }
+                    else{
+                        _log.debug("Input stream was null for "+authFilePath);
+                    }
+                    _log.debug("Found "+props.size()+ " properties.");
                     HashMap<String, String> newProv = new HashMap<String, String>();
-
-                    final Properties provider = PropertiesLoaderUtils.loadProperties(resource);
-                    for (Map.Entry<Object, Object> providerProperty : provider.entrySet()) {
+                    for (Map.Entry<Object, Object> providerProperty : props.entrySet()) {
+                        _log.debug("Trying to add property "+providerProperty.getKey().toString()+" with value "+providerProperty.getValue().toString());
                         newProv.put(providerProperty.getKey().toString(), providerProperty.getValue().toString());
                     }
                     providerList.add(newProv);
+                    _log.debug("Added provider (name:"+newProv.get("name")+", id:"+newProv.get("id")+", type:"+newProv.get("type")+").");
                 }
             }
+
+            //If the provider list is still empty, try to get providers from plugins
+            if(providerList==null || providerList.isEmpty()){
+                //If no properties files were found in the config directories, look for properties files that might be from plugins
+                resources   = BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/auth/**/*" + filenameEnd);
+                if(resources==null || resources.isEmpty()){
+                    String            dbName        = "Database";
+                    String            dbId          = "localdb";
+                    String            dbType        = "db";
+                    HashMap<String, String> dbProv = new HashMap<String, String>();
+                    dbProv.put("name", dbName);
+                    dbProv.put("id", dbId);
+                    dbProv.put("type", dbType);
+                    providerList.add(dbProv);
+                }
+                else {
+                    for (final Resource resource : resources) {
+                        String filename = resource.getFilename();
+                        String id = filename.substring(0, (filename.length() - filenameEnd.length()));
+                        HashMap<String, String> newProv = new HashMap<String, String>();
+
+                        final Properties provider = PropertiesLoaderUtils.loadProperties(resource);
+                        for (Map.Entry<Object, Object> providerProperty : provider.entrySet()) {
+                            newProv.put(providerProperty.getKey().toString(), providerProperty.getValue().toString());
+                        }
+                        providerList.add(newProv);
+                    }
+                }
+            }
+
+
         } catch (Exception e) {
-            _log.error("", e);
+            _log.error("Error getting authentication provider properties", e);
         }
 
-        // Create providers
+        // Create providers from provider list
         for (HashMap<String, String> prov : providerList) {
             String name = prov.get("name");
             String id   = prov.get("id");
diff --git a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java
index 027b63080aa0d5a6d5e42003bad7a5769ef65511..e9f1f38eeb3a74cc94ef9d04a206cee89afc4832 100644
--- a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java
+++ b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java
@@ -36,6 +36,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
@@ -86,33 +87,49 @@ public class XnatAppInfo {
         }
 
         try (final InputStream input = context.getResourceAsStream("/META-INF/MANIFEST.MF")) {
-            final Manifest manifest = new Manifest(input);
-            final Attributes attributes = manifest.getMainAttributes();
-            _properties.setProperty("buildNumber", attributes.getValue("Build-Number"));
-            _properties.setProperty("buildDate", attributes.getValue("Build-Date"));
-            _properties.setProperty("version", attributes.getValue("Implementation-Version"));
-            _properties.setProperty("commit", attributes.getValue("Implementation-Sha"));
-            if (_log.isDebugEnabled()) {
-                _log.debug("Initialized application build information:\n * Version: {}\n * Build number: {}\n * Build Date: {}\n * Commit: {}",
-                           _properties.getProperty("version"),
-                           _properties.getProperty("buildNumber"),
-                           _properties.getProperty("buildDate"),
-                           _properties.getProperty("commit"));
-            }
-            for (final Object key : attributes.keySet()) {
-                final String name = key.toString();
-                if (!PRIMARY_MANIFEST_ATTRIBUTES.contains(name)) {
-                    _properties.setProperty(name, attributes.getValue(name));
+            if (input != null) {
+                final Manifest manifest = new Manifest(input);
+                final Attributes attributes = manifest.getMainAttributes();
+                _properties.setProperty("buildNumber", attributes.getValue("Build-Number"));
+                _properties.setProperty("buildDate", attributes.getValue("Build-Date"));
+                _properties.setProperty("version", attributes.getValue("Implementation-Version"));
+                _properties.setProperty("commit", attributes.getValue("Implementation-Sha"));
+                if (_log.isDebugEnabled()) {
+                    _log.debug("Initialized application build information:\n * Version: {}\n * Build number: {}\n * Build Date: {}\n * Commit: {}",
+                               _properties.getProperty("version"),
+                               _properties.getProperty("buildNumber"),
+                               _properties.getProperty("buildDate"),
+                               _properties.getProperty("commit"));
                 }
-            }
-            final Map<String, Attributes> entries = manifest.getEntries();
-            for (final String key : entries.keySet()) {
-                final Map<String, String> keyedAttributes = new HashMap<>();
-                _attributes.put(key, keyedAttributes);
-                final Attributes entry = entries.get(key);
-                for (final Object subkey : entry.keySet()) {
-                    final String property = (String) subkey;
-                    keyedAttributes.put(property, attributes.getValue(property));
+                for (final Object key : attributes.keySet()) {
+                    final String name = key.toString();
+                    if (!PRIMARY_MANIFEST_ATTRIBUTES.contains(name)) {
+                        _properties.setProperty(name, attributes.getValue(name));
+                    }
+                }
+                final Map<String, Attributes> entries = manifest.getEntries();
+                for (final String key : entries.keySet()) {
+                    final Map<String, String> keyedAttributes = new HashMap<>();
+                    _attributes.put(key, keyedAttributes);
+                    final Attributes entry = entries.get(key);
+                    for (final Object subkey : entry.keySet()) {
+                        final String property = (String) subkey;
+                        keyedAttributes.put(property, attributes.getValue(property));
+                    }
+                }
+            } else {
+                _log.warn("Attempted to load /META-INF/MANIFEST.MF but couldn't find it, all version information is unknown.");
+                _properties.setProperty("buildNumber", "Unknown");
+                _properties.setProperty("buildDate", FORMATTER.format(new Date()));
+                _properties.setProperty("version", "Unknown");
+                _properties.setProperty("commit", "Unknown");
+                if (_log.isDebugEnabled()) {
+                    _log.debug("Initialized application build information:\n * Version: {}\n * Build number: {}\n * Build Date: {}\n * Commit: {}",
+                               _properties.getProperty("version"),
+                               _properties.getProperty("buildNumber"),
+                               _properties.getProperty("buildDate"),
+                               _properties.getProperty("commit"));
+
                 }
             }
             if (!isInitialized()) {
@@ -240,6 +257,7 @@ public class XnatAppInfo {
      *
      * @return The value of the property if found, null otherwise.
      */
+    @SuppressWarnings("unused")
     public String getConfiguredProperty(final String property) {
         return getConfiguredProperty(property, (String) null);
     }
@@ -265,6 +283,7 @@ public class XnatAppInfo {
      *
      * @return The value of the property if found, null otherwise.
      */
+    @SuppressWarnings("unused")
     public <T> T getConfiguredProperty(final String property, final Class<T> type) {
         return getConfiguredProperty(property, type, null);
     }
@@ -511,8 +530,9 @@ public class XnatAppInfo {
 
     private static final Logger _log = LoggerFactory.getLogger(XnatAppInfo.class);
 
-    private static final List<String>   PRIMARY_MANIFEST_ATTRIBUTES = Arrays.asList("Build-Number", "Build-Date", "Implementation-Version", "Implementation-Sha");
-    private static final ResourceLoader RESOURCE_LOADER             = new DefaultResourceLoader();
+    private static final List<String>     PRIMARY_MANIFEST_ATTRIBUTES = Arrays.asList("Build-Number", "Build-Date", "Implementation-Version", "Implementation-Sha");
+    private static final ResourceLoader   RESOURCE_LOADER             = new DefaultResourceLoader();
+    private static final SimpleDateFormat FORMATTER                   = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a");
 
     private final JdbcTemplate _template;
     private final Environment  _environment;
diff --git a/src/main/java/org/nrg/xnat/services/archive/CatalogService.java b/src/main/java/org/nrg/xnat/services/archive/CatalogService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c923e98d26d311bb2f7654bc2a6a60b2c6cb3de
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/services/archive/CatalogService.java
@@ -0,0 +1,121 @@
+/*
+ * web: org.nrg.xnat.services.archive.CatalogService
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.services.archive;
+
+import org.nrg.action.ClientException;
+import org.nrg.action.ServerException;
+import org.nrg.xft.security.UserI;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Defines the service that maintains and manages the XNAT archive catalog.
+ */
+public interface CatalogService {
+    /**
+     * Specifies an operation to be performed during a {@link #refreshResourceCatalogs(UserI, List, Operation...) catalog
+     * refresh}.
+     */
+    enum Operation {
+        All,
+        Append,
+        Checksum,
+        Delete,
+        PopulateStats;
+
+        public static final List<Operation> ALL = Arrays.asList(Operation.values());
+    }
+
+    /**
+     * Refreshes the catalog for the specified resource. The resource should be identified by standard archive-relative
+     * paths, e.g.:
+     *
+     * <pre>
+     * {@code
+     * /archive/experiments/XNAT_E0001
+     * /archive/projects/XNAT_01/subjects/XNAT_01_01
+     * }
+     * </pre>
+     *
+     * @param user       The user performing the refresh operation.
+     * @param resource   The path to the resource to be refreshed.
+     * @param operations One or more operations to perform. If no operations are specified, {@link Operation#All} is
+     *                   presumed.
+     *
+     * @throws ClientException When an error occurs that is caused somehow by the requested operation.
+     * @throws ServerException When an error occurs in the system during the refresh operation.
+     */
+    void refreshResourceCatalog(final UserI user, final String resource, final Operation... operations) throws ServerException, ClientException;
+
+    /**
+     * Refreshes the catalog for the specified resource. The resource should be identified by standard archive-relative
+     * paths, e.g.:
+     *
+     * <pre>
+     * {@code
+     * /archive/experiments/XNAT_E0001
+     * /archive/projects/XNAT_01/subjects/XNAT_01_01
+     * }
+     * </pre>
+     *
+     * @param user       The user performing the refresh operation.
+     * @param resource   The path to the resource to be refreshed.
+     * @param operations One or more operations to perform. If no operations are specified, {@link Operation#All} is
+     *                   presumed.
+     *
+     * @throws ClientException When an error occurs that is caused somehow by the requested operation.
+     * @throws ServerException When an error occurs in the system during the refresh operation.
+     */
+    void refreshResourceCatalog(final UserI user, final String resource, final Collection<Operation> operations) throws ServerException, ClientException;
+
+    /**
+     * Refreshes the catalog for the specified resources. The resources should be identified by
+     * standard archive-relative paths, e.g.:
+     *
+     * <pre>
+     * {@code
+     * /archive/experiments/XNAT_E0001
+     * /archive/projects/XNAT_01/subjects/XNAT_01_01
+     * }
+     * </pre>
+     *
+     * @param user       The user performing the refresh operation.
+     * @param resources  The paths to the resources to be refreshed.
+     * @param operations One or more operations to perform. If no operations are specified, {@link Operation#All} is
+     *                   presumed.
+     *
+     * @throws ClientException When an error occurs that is caused somehow by the requested operation.
+     * @throws ServerException When an error occurs in the system during the refresh operation.
+     */
+    void refreshResourceCatalogs(final UserI user, final List<String> resources, final Operation... operations) throws ServerException, ClientException;
+
+    /**
+     * Refreshes the catalog for the specified resources. The resources should be identified by
+     * standard archive-relative paths, e.g.:
+     *
+     * <pre>
+     * {@code
+     * /archive/experiments/XNAT_E0001
+     * /archive/projects/XNAT_01/subjects/XNAT_01_01
+     * }
+     * </pre>
+     *
+     * @param user       The user performing the refresh operation.
+     * @param resources  The paths to the resources to be refreshed.
+     * @param operations One or more operations to perform. If no operations are specified, {@link Operation#All} is
+     *                   presumed.
+     *
+     * @throws ClientException When an error occurs that is caused somehow by the requested operation.
+     * @throws ServerException When an error occurs in the system during the refresh operation.
+     */
+    void refreshResourceCatalogs(final UserI user, final List<String> resources, final Collection<Operation> operations) throws ServerException, ClientException;
+}
diff --git a/src/main/java/org/nrg/xnat/services/archive/impl/legacy/DefaultCatalogService.java b/src/main/java/org/nrg/xnat/services/archive/impl/legacy/DefaultCatalogService.java
new file mode 100644
index 0000000000000000000000000000000000000000..586706e20d3ba471303a0f902fcb80ea0cc719f9
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/services/archive/impl/legacy/DefaultCatalogService.java
@@ -0,0 +1,242 @@
+/*
+ * web: org.nrg.xnat.services.archive.impl.legacy.DefaultCatalogService
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine
+ * All Rights Reserved
+ *  
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.services.archive.impl.legacy;
+
+import org.apache.commons.lang3.StringUtils;
+import org.nrg.action.ClientException;
+import org.nrg.action.ServerException;
+import org.nrg.xdat.bean.CatCatalogBean;
+import org.nrg.xdat.model.XnatAbstractresourceI;
+import org.nrg.xdat.om.XnatAbstractresource;
+import org.nrg.xdat.om.XnatResourcecatalog;
+import org.nrg.xdat.om.base.BaseXnatExperimentdata;
+import org.nrg.xdat.security.helpers.Permissions;
+import org.nrg.xft.event.EventDetails;
+import org.nrg.xft.event.EventMetaI;
+import org.nrg.xft.event.EventUtils;
+import org.nrg.xft.event.persist.PersistentWorkflowI;
+import org.nrg.xft.event.persist.PersistentWorkflowUtils;
+import org.nrg.xft.security.UserI;
+import org.nrg.xnat.helpers.uri.URIManager;
+import org.nrg.xnat.helpers.uri.UriParserUtils;
+import org.nrg.xnat.helpers.uri.archive.ResourceURII;
+import org.nrg.xnat.services.archive.CatalogService;
+import org.nrg.xnat.turbine.utils.ArchivableItem;
+import org.nrg.xnat.utils.CatalogUtils;
+import org.nrg.xnat.utils.WorkflowUtils;
+import org.restlet.data.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@inheritDoc}
+ */
+@Service
+public class DefaultCatalogService implements CatalogService {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void refreshResourceCatalog(final UserI user, final String resource, final Operation... operations) throws ServerException, ClientException {
+        _refreshCatalog(user, resource, Arrays.asList(operations));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void refreshResourceCatalog(final UserI user, final String resource, final Collection<Operation> operations) throws ServerException, ClientException {
+        _refreshCatalog(user, resource, operations);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void refreshResourceCatalogs(final UserI user, final List<String> resources, final Operation... operations) throws ServerException, ClientException {
+        for (final String resource : resources) {
+            _refreshCatalog(user, resource, Arrays.asList(operations));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void refreshResourceCatalogs(final UserI user, final List<String> resources, final Collection<Operation> operations) throws ServerException, ClientException {
+        for (final String resource : resources) {
+            _refreshCatalog(user, resource, operations);
+        }
+    }
+
+    /**
+     * Performs the actual work of refreshing a single catalog.
+     *
+     * @param user          The user requesting the refresh operation.
+     * @param resource      The archive path for the resource to refresh.
+     * @param operations    The operations to be performed.
+     *
+     * @throws ClientException When an error occurs that is caused somehow by the requested operation.
+     * @throws ServerException When an error occurs in the system during the refresh operation.
+     */
+    private void _refreshCatalog(final UserI user, final String resource, final Collection<Operation> operations) throws ServerException, ClientException {
+        try {
+            //parse passed URI parameter
+            final URIManager.DataURIA uri = UriParserUtils.parseURI(resource);
+
+            if (!(uri instanceof URIManager.ArchiveItemURI)) {
+                throw new ClientException("Invalid Resource URI:" + resource);
+            }
+
+            URIManager.ArchiveItemURI resourceURI = (URIManager.ArchiveItemURI) uri;
+
+            ArchivableItem existenceCheck = resourceURI.getSecurityItem();
+
+            if (existenceCheck != null) {
+                final EventDetails event = new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.PROCESS, "Catalog(s) Refreshed", "Refreshed catalog for resource " + resourceURI.getUri(), "");
+
+                final Collection<Operation> list = getOperations(operations);
+
+                final boolean append = list.contains(Operation.Append);
+                final boolean checksum = list.contains(Operation.Checksum);
+                final boolean delete = list.contains(Operation.Delete);
+                final boolean populateStats = list.contains(Operation.PopulateStats);
+
+                try {
+                    if (resourceURI instanceof ResourceURII) {//if we are referencing a specific catalog, make sure it doesn't actually reference an individual file.
+                        if (StringUtils.isNotEmpty(((ResourceURII) resourceURI).getResourceFilePath()) && !((ResourceURII) resourceURI).getResourceFilePath().equals("/")) {
+                            throw new ClientException(Status.CLIENT_ERROR_BAD_REQUEST, new Exception("This operation cannot be performed directly on a file URL"));
+                        }
+                    }
+
+                    final ArchivableItem security = resourceURI.getSecurityItem();
+                    try {
+                        if (!Permissions.canEdit(user, security)) {
+                            throw new ClientException(Status.CLIENT_ERROR_FORBIDDEN, new Exception("Unauthorized attempt to add a file to " + resourceURI.getUri()));
+                        }
+                    } catch (ClientException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        _log.error("An error occurred trying to check the edit permissions for user " + user.getUsername(), e);
+                    }
+
+                    final PersistentWorkflowI wrk = PersistentWorkflowUtils.getOrCreateWorkflowData(null, user, resourceURI.getSecurityItem().getItem(), event);
+
+                    for (XnatAbstractresourceI res : resourceURI.getResources(true)) {
+                        final String archiveRootPath = resourceURI.getSecurityItem().getArchiveRootPath();
+                        refreshResourceCatalog((XnatAbstractresource) res, archiveRootPath, populateStats, checksum, delete, append, user, wrk.buildEvent());
+                    }
+
+                    WorkflowUtils.complete(wrk, wrk.buildEvent());
+                } catch (PersistentWorkflowUtils.JustificationAbsent justificationAbsent) {
+                    throw new ClientException("No justification was provided for the refresh action, but is required by the system configuration.");
+                } catch (PersistentWorkflowUtils.ActionNameAbsent actionNameAbsent) {
+                    throw new ClientException("No action name was provided for the refresh action, but is required by the system configuration.");
+                } catch (PersistentWorkflowUtils.IDAbsent idAbsent) {
+                    throw new ClientException("No workflow ID was provided for the refresh action, but is required by the system configuration.");
+                } catch (BaseXnatExperimentdata.UnknownPrimaryProjectException e) {
+                    throw new ClientException("Couldn't find the primary project for the specified resource " + resource);
+                } catch (Exception e) {
+                    throw new ServerException("An error occurred trying to save the workflow for the refresh operation.", e);
+                }
+            }
+        } catch (MalformedURLException e) {
+            throw new ClientException("Invalid Resource URI:" + resource);
+        }
+    }
+
+    private boolean refreshResourceCatalog(final XnatAbstractresource resource,
+                                           final String projectPath,
+                                           final boolean populateStats,
+                                           final boolean checksums,
+                                           final boolean removeMissingFiles,
+                                           final boolean addUnreferencedFiles,
+                                           final UserI user, final EventMetaI now) throws ServerException {
+        if (resource instanceof XnatResourcecatalog) {
+            final XnatResourcecatalog catRes = (XnatResourcecatalog) resource;
+
+            final CatCatalogBean cat = CatalogUtils.getCatalog(projectPath, catRes);
+            final File catFile = CatalogUtils.getCatalogFile(projectPath, catRes);
+
+            if (cat != null) {
+                boolean modified = false;
+
+                if (addUnreferencedFiles) {//check for files in the proper resource directory, but not referenced from the existing xml
+                    if (CatalogUtils.addUnreferencedFiles(catFile, cat, user, now.getEventId())) {
+                        modified = true;
+                    }
+                }
+
+                if (CatalogUtils.formalizeCatalog(cat, catFile.getParent(), user, now, checksums, removeMissingFiles)) {
+                    modified = true;
+                }
+
+                if (modified) {
+                    try {
+                        CatalogUtils.writeCatalogToFile(cat, catFile, checksums);
+                    } catch (Exception e) {
+                        throw new ServerException("An error occurred writing the catalog file " + catFile.getAbsolutePath(), e);
+                    }
+                }
+
+                // populate (or repopulate) the file stats --- THIS SHOULD BE DONE AFTER modifications to the catalog xml
+                if (populateStats || modified) {
+                    if (CatalogUtils.populateStats(resource, projectPath) || modified) {
+                        try {
+                            if (resource.save(user, false, false, now)) {
+                                modified = true;
+                            }
+                        } catch (Exception e) {
+                            throw new ServerException("An error occurred saving the resource " + resource.getFullPath(projectPath), e);
+                        }
+                    }
+                }
+
+                return modified;
+            }
+        } else if (populateStats) {
+            if (CatalogUtils.populateStats(resource, projectPath)) {
+                try {
+                    return resource.save(user, false, false, now);
+                } catch (Exception e) {
+                    throw new ServerException("An error occurred saving the resource " + resource.getFullPath(projectPath), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private static Collection<Operation> getOperations(final Collection<Operation> operations) {
+        // The default is All, so if they specified nothing, give them all.
+        if (operations == null || operations.size() == 0) {
+            return Operation.ALL;
+        }
+
+        // If ANY of the operations are All, give them all as well.
+        for (final Operation operation : operations) {
+            if (operation == Operation.All) {
+                return Operation.ALL;
+            }
+        }
+
+        // If All isn't specified, just return a list of the actual values.
+        return operations;
+    }
+
+    private static final Logger _log = LoggerFactory.getLogger(DefaultCatalogService.class);
+}
diff --git a/src/main/java/org/nrg/xnat/services/impl/hibernate/HibernateHostInfoService.java b/src/main/java/org/nrg/xnat/services/impl/hibernate/HibernateHostInfoService.java
deleted file mode 100644
index 50f5e0228b8f0acb9b5c263f38b265da76acb92c..0000000000000000000000000000000000000000
--- a/src/main/java/org/nrg/xnat/services/impl/hibernate/HibernateHostInfoService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * web: org.nrg.xnat.services.impl.hibernate.HibernateHostInfoService
- * XNAT http://www.xnat.org
- * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
- * All Rights Reserved
- *
- * Released under the Simplified BSD.
- */
-
-/*
- * 
- */
-package org.nrg.xnat.services.impl.hibernate;
-
-import org.nrg.framework.orm.hibernate.AbstractHibernateEntityService;
-import org.nrg.xdat.XDAT;
-import org.nrg.xnat.daos.HostInfoDAO;
-import org.nrg.xnat.entities.HostInfo;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * The Class HibernateHostInfoService.
- */
-@Service
-public class HibernateHostInfoService extends AbstractHibernateEntityService<HostInfo, HostInfoDAO> {
-
-	/** The _instance. */
-	private static HibernateHostInfoService _instance;
-	
-	/**
-	 * Instantiates a new hibernate host info service.
-	 */
-	public HibernateHostInfoService() {
-		_instance = this;
-	}
-	
-	/**
-	 * Gets the service.
-	 *
-	 * @return the service
-	 */
-	public static HibernateHostInfoService getService() {
-	    if (_instance == null) {
-	    	_instance = XDAT.getContextService().getBean(HibernateHostInfoService.class);
-	    }
-	    return _instance;
-	}
-	
-    /**
-     * Gets the host number.
-     *
-     * @return the host number
-     */
-    @Transactional
-    public String getHostNumber() {
-        return getDao().getHostNumber();
-    }
-
-    /**
-     * Gets the host number.
-     *
-     * @param hostName the host name
-     * @return the host number
-     */
-    @Transactional
-    public String getHostNumber(String hostName) {
-        return getDao().getHostNumber(hostName);
-    }
-
-}
diff --git a/src/main/java/org/nrg/xnat/services/system/HostInfoService.java b/src/main/java/org/nrg/xnat/services/system/HostInfoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbead065d08f6c9b7b0b402d2988caaa99d82f1b
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/services/system/HostInfoService.java
@@ -0,0 +1,34 @@
+/*
+ * web: org.nrg.xnat.services.system.HostInfoService
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+package org.nrg.xnat.services.system;
+
+import org.nrg.framework.orm.hibernate.BaseHibernateService;
+import org.nrg.xnat.entities.HostInfo;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Provides information about the current host.
+ */
+public interface HostInfoService extends BaseHibernateService<HostInfo> {
+    /**
+     * Gets the host number of the current host.
+     *
+     * @return The host number.
+     */
+    String getHostNumber();
+
+    /**
+     * Gets the host number of the host with the indicated name.
+     *
+     * @param hostName The name of the host.
+     * @return The host number of the indicated host.
+     */
+    String getHostNumber(String hostName);
+}
diff --git a/src/main/java/org/nrg/xnat/services/system/impl/hibernate/HibernateHostInfoService.java b/src/main/java/org/nrg/xnat/services/system/impl/hibernate/HibernateHostInfoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..88fbad3291ee2833c4c81c9cf36fc2db21bcb0e0
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/services/system/impl/hibernate/HibernateHostInfoService.java
@@ -0,0 +1,44 @@
+/*
+ * web: org.nrg.xnat.services.system.impl.hibernate.HibernateHostInfoService
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ */
+
+/*
+ * 
+ */
+package org.nrg.xnat.services.system.impl.hibernate;
+
+import org.nrg.framework.orm.hibernate.AbstractHibernateEntityService;
+import org.nrg.xnat.daos.HostInfoDAO;
+import org.nrg.xnat.entities.HostInfo;
+import org.nrg.xnat.services.system.HostInfoService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * {@inheritDoc}
+ */
+@Service
+public class HibernateHostInfoService extends AbstractHibernateEntityService<HostInfo, HostInfoDAO> implements HostInfoService {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @Transactional
+    public String getHostNumber() {
+        return getDao().getHostNumber();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @Transactional
+    public String getHostNumber(String hostName) {
+        return getDao().getHostNumber(hostName);
+    }
+}
diff --git a/src/main/java/org/nrg/xnat/turbine/modules/actions/ExptFileUpload.java b/src/main/java/org/nrg/xnat/turbine/modules/actions/ExptFileUpload.java
index cfaa49dbb2f7a68c29b3f15e1773cb3bf1bd9b24..267d13d3bc33c88e05c7042998be1ec8818e42e2 100644
--- a/src/main/java/org/nrg/xnat/turbine/modules/actions/ExptFileUpload.java
+++ b/src/main/java/org/nrg/xnat/turbine/modules/actions/ExptFileUpload.java
@@ -9,22 +9,12 @@
 
 package org.nrg.xnat.turbine.modules.actions;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Calendar;
-import java.util.zip.ZipOutputStream;
-
-import javax.servlet.http.HttpSession;
-
 import org.apache.commons.fileupload.FileItem;
 import org.apache.log4j.Logger;
 import org.apache.turbine.util.RunData;
 import org.apache.turbine.util.parser.ParameterParser;
 import org.apache.velocity.context.Context;
+import org.nrg.xdat.XDAT;
 import org.nrg.xdat.bean.CatCatalogBean;
 import org.nrg.xdat.bean.CatCatalogTagBean;
 import org.nrg.xdat.bean.base.BaseElement;
@@ -46,15 +36,17 @@ import org.nrg.xft.utils.SaveItemHelper;
 import org.nrg.xft.utils.zip.TarUtils;
 import org.nrg.xft.utils.zip.ZipI;
 import org.nrg.xft.utils.zip.ZipUtils;
-import org.nrg.xnat.helpers.uri.URIManager;
-import org.nrg.xnat.helpers.uri.URIManager.ArchiveItemURI;
-import org.nrg.xnat.helpers.uri.UriParserUtils;
+import org.nrg.xnat.services.archive.CatalogService;
 import org.nrg.xnat.turbine.utils.ArcSpecManager;
 import org.nrg.xnat.turbine.utils.XNATUtils;
-import org.nrg.xnat.utils.ResourceUtils;
 import org.nrg.xnat.utils.WorkflowUtils;
 import org.xml.sax.SAXException;
 
+import javax.servlet.http.HttpSession;
+import java.io.*;
+import java.util.Calendar;
+import java.util.zip.ZipOutputStream;
+
 public class ExptFileUpload extends SecureAction {
 
     private static final Logger logger = Logger.getLogger(ExptFileUpload.class);
@@ -411,12 +403,8 @@ public class ExptFileUpload extends SecureAction {
                     data.getParameters().setString("project", tempMR.getProject());
                 }
 
-// New code to refresh the file catalog
-                URIManager.DataURIA uri=UriParserUtils.parseURI("/archive/experiments/" + tempMR.getId());
-                ArchiveItemURI resourceURI = (ArchiveItemURI) uri;
-                UserI user = TurbineUtils.getUser(data);
-                ResourceUtils.refreshResourceCatalog(resourceURI, user, this.newEventInstance(data, EventUtils.CATEGORY.DATA, "Catalog(s) Refreshed"), true, true, true, true);
-// End new code
+                final CatalogService catalogService = XDAT.getContextService().getBean(CatalogService.class);
+                catalogService.refreshResourceCatalog(XDAT.getUserDetails(), "/archive/experiments/" + tempMR.getId());
 
                 if (TurbineUtils.HasPassedParameter("destination", data)){
                     this.redirectToReportScreen((String)TurbineUtils.GetPassedParameter("destination", data), tempMR.getItem(), data);
diff --git a/src/main/java/org/nrg/xnat/turbine/modules/actions/ManagePipeline.java b/src/main/java/org/nrg/xnat/turbine/modules/actions/ManagePipeline.java
index 74253496a9cf28f35b9cc89f25f4f73951c5f962..7dd6b042a21da18da5a7ad35f97d3f6abab99a6f 100644
--- a/src/main/java/org/nrg/xnat/turbine/modules/actions/ManagePipeline.java
+++ b/src/main/java/org/nrg/xnat/turbine/modules/actions/ManagePipeline.java
@@ -379,7 +379,7 @@ public class ManagePipeline extends SecureAction {
             xnatPipelineLauncher.setParameter("useremail", user.getEmail());
             xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
             xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
-            xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+            xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
             xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
             xnatPipelineLauncher.setPipelineName(pipeline_path);
             String exptLabel = item.getStringProperty("label");
diff --git a/src/main/java/org/nrg/xnat/turbine/modules/actions/PipelineActions.java b/src/main/java/org/nrg/xnat/turbine/modules/actions/PipelineActions.java
index 46433009b3c0685fe250c89638a6859b256eb12c..208a02ea5629e32b62554af8f8ea9f024834bee9 100644
--- a/src/main/java/org/nrg/xnat/turbine/modules/actions/PipelineActions.java
+++ b/src/main/java/org/nrg/xnat/turbine/modules/actions/PipelineActions.java
@@ -204,7 +204,7 @@ public class PipelineActions extends SecureAction{
         xnatPipelineLauncher.setParameter("userfullname", XnatPipelineLauncher.getUserName(user));
         xnatPipelineLauncher.setParameter("adminemail", XDAT.getSiteConfigPreferences().getAdminEmail());
         xnatPipelineLauncher.setParameter("xnatserver", TurbineUtils.GetSystemName());
-        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+        xnatPipelineLauncher.setParameter("mailhost", XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
 
         String emailsStr =  ((String)org.nrg.xdat.turbine.utils.TurbineUtils.GetPassedParameter("emailField",data));
         if (emailsStr != null) {
diff --git a/src/main/java/org/nrg/xnat/turbine/modules/actions/QDECAction.java b/src/main/java/org/nrg/xnat/turbine/modules/actions/QDECAction.java
index d753fc8fb8f5ca0b7826a156d97a64a41ce51947..0888c646368e5ba06abb7266e32bfe79caa8081c 100644
--- a/src/main/java/org/nrg/xnat/turbine/modules/actions/QDECAction.java
+++ b/src/main/java/org/nrg/xnat/turbine/modules/actions/QDECAction.java
@@ -208,7 +208,7 @@ public class QDECAction extends ListingAction{
         
         param = parameters.addNewParameter();
         param.setName("mailhost");
-        param.addNewValues().setUnique( XDAT.getNotificationsPreferences().getSmtpServer().get("host"));
+        param.addNewValues().setUnique( XDAT.getNotificationsPreferences().getSmtpServer().getHostname());
         
         return parameters;
         
diff --git a/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java b/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java
index a5a704ab0be9698755353015ef4927b9774cbd85..6573985c6413b5c087c6aad36f4f5756d44e86dd 100644
--- a/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java
+++ b/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java
@@ -15,6 +15,7 @@ import org.nrg.xdat.model.ArcProjectI;
 import org.nrg.xdat.om.ArcArchivespecification;
 import org.nrg.xdat.preferences.NotificationsPreferences;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
+import org.nrg.xdat.preferences.SmtpServer;
 import org.nrg.xft.event.EventDetails;
 import org.nrg.xft.event.EventUtils;
 import org.nrg.xft.security.UserI;
@@ -31,7 +32,6 @@ import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author timo
@@ -61,20 +61,22 @@ public class ArcSpecManager {
             arcSpec = GetFreshInstance();
 
             try {
-                if (arcSpec!=null){
-                    String cachePath = arcSpec.getGlobalCachePath();
-                    if (cachePath!=null){
-                        File f = new File(cachePath,"archive_specification.xml");
-                        f.getParentFile().mkdirs();
-                        FileWriter fw = new FileWriter(f);
-
-                        arcSpec.toXML(fw, true);
-                        fw.flush();
-                        fw.close();
+                if (arcSpec != null) {
+                    final String cachePath = arcSpec.getGlobalCachePath();
+                    if (StringUtils.isNotBlank(cachePath)) {
+                        final File arcSpecFile       = new File(cachePath, "archive_specification.xml");
+                        final File arcSpecFileFolder = arcSpecFile.getParentFile();
+                        if (!arcSpecFileFolder.exists() && !arcSpecFileFolder.mkdirs()) {
+                            throw new RuntimeException("Failed to create working file " + arcSpecFile.getAbsolutePath() + ", please check permissions and file system.");
+                        }
+                        logger.debug("Initializing arcspec to cache file {}", arcSpecFile.getAbsolutePath());
+                        try (FileWriter writer = new FileWriter(arcSpecFile)) {
+                            arcSpec.toXML(writer, true);
+                        }
                     }
                 }
             } catch (IllegalArgumentException | IOException | SAXException e) {
-                logger.error("",e);
+                logger.error("", e);
             }
             logger.debug("Done writing out arc spec.");
    
@@ -120,12 +122,11 @@ public class ArcSpecManager {
             arcSpec.setSiteUrl(siteConfigPreferences.getSiteUrl());
         }
 
-        final Map<String, String> smtpServer = notificationsPreferences.getSmtpServer();
-        if (smtpServer != null && smtpServer.containsKey("host")) {
-            if (logger.isInfoEnabled()) {
-                logger.info("Setting SMTP host to: {}", smtpServer.get("host"));
-            }
-            arcSpec.setSmtpHost(smtpServer.get("host"));
+        final SmtpServer smtpServer = notificationsPreferences.getSmtpServer();
+        if (smtpServer != null) {
+            final String hostname = smtpServer.getHostname();
+            logger.info("Setting SMTP host to: {}", hostname);
+            arcSpec.setSmtpHost(hostname);
         }
 
         if (logger.isInfoEnabled()) {
diff --git a/src/main/java/org/nrg/xnat/turbine/utils/IDGenerator.java b/src/main/java/org/nrg/xnat/turbine/utils/IDGenerator.java
index 0200d7c39c097afd7957f95442454d1689097d92..867315c19eaaaf9bc2bf2e9b15100f1817a8ba2e 100644
--- a/src/main/java/org/nrg/xnat/turbine/utils/IDGenerator.java
+++ b/src/main/java/org/nrg/xnat/turbine/utils/IDGenerator.java
@@ -13,7 +13,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.nrg.xdat.XDAT;
 import org.nrg.xft.XFTTable;
 import org.nrg.xft.identifier.IDGeneratorI;
-import org.nrg.xnat.services.impl.hibernate.HibernateHostInfoService;
+import org.nrg.xnat.services.system.HostInfoService;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,7 +46,7 @@ public class IDGenerator implements IDGeneratorI {
 	 */
 	private static String getHostInfo(){
 		if (hostInfo==null || hostInfo.isEmpty()) {
-			hostInfo =  HibernateHostInfoService.getService().getHostNumber();
+			hostInfo =  XDAT.getContextService().getBean(HostInfoService.class).getHostNumber();
 		}
 		return hostInfo;
 	}
@@ -178,5 +178,4 @@ public class IDGenerator implements IDGeneratorI {
 		site_id = StringUtils.replace(site_id, "^", "");
 		return site_id;
 	}
-	
 }
diff --git a/src/main/java/org/nrg/xnat/utils/ResourceUtils.java b/src/main/java/org/nrg/xnat/utils/ResourceUtils.java
deleted file mode 100644
index 42099a9deafc9335f0600c6525fd9172a802e7b3..0000000000000000000000000000000000000000
--- a/src/main/java/org/nrg/xnat/utils/ResourceUtils.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * web: org.nrg.xnat.utils.ResourceUtils
- * XNAT http://www.xnat.org
- * Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
- * All Rights Reserved
- *
- * Released under the Simplified BSD.
- */
-
-package org.nrg.xnat.utils;
-
-import org.apache.commons.lang3.StringUtils;
-import org.nrg.action.ActionException;
-import org.nrg.action.ClientException;
-import org.nrg.action.ServerException;
-import org.nrg.xdat.bean.CatCatalogBean;
-import org.nrg.xdat.model.XnatAbstractresourceI;
-import org.nrg.xdat.om.XnatAbstractresource;
-import org.nrg.xdat.om.XnatResourcecatalog;
-import org.nrg.xdat.om.base.BaseXnatExperimentdata.UnknownPrimaryProjectException;
-import org.nrg.xdat.security.helpers.Permissions;
-import org.nrg.xft.event.EventDetails;
-import org.nrg.xft.event.EventMetaI;
-import org.nrg.xft.event.persist.PersistentWorkflowI;
-import org.nrg.xft.event.persist.PersistentWorkflowUtils;
-import org.nrg.xft.exception.InvalidItemException;
-import org.nrg.xft.security.UserI;
-import org.nrg.xnat.helpers.uri.URIManager.ArchiveItemURI;
-import org.nrg.xnat.helpers.uri.archive.ResourceURII;
-import org.nrg.xnat.turbine.utils.ArchivableItem;
-import org.restlet.data.Status;
-
-import java.io.File;
-
-public class ResourceUtils {
-	
-	/**
-	 * Refresh values in resource catalog 
-     * @param resource                The resource to be updated. In spite of the name, this will also work with
-     *                                {@link XnatAbstractresource} objects as well, populating the persisted file
-     *                                statistics from the actual data on disk.
-     * @param projectPath             The location of the archive path for the project to be refreshed.
-     * @param checksums               Indicates whether checksums should be calculated for catalog refresh operations.
-     * @param removeMissingFiles      Indicates whether catalog entries for files that no longer exist should be
-     *                                removed.
-     * @param addUnreferencedFiles    Indicates whether entries should be added for files that are in the archive but
-     *                                are not already in the catalog.
-     * @param user                    The user who requested the refresh operation.
-	 * @param now                     Event metadata for the operation.
-	 * @return true if the catalog was modified
-	 * @throws Exception Failures stem from the save operation.
-	 */
-	public static boolean refreshResourceCatalog(final XnatAbstractresource resource, final String projectPath, final boolean populateStats, final boolean checksums, final boolean removeMissingFiles, final boolean addUnreferencedFiles, final UserI user, final EventMetaI now) throws Exception {
-		if(resource instanceof XnatResourcecatalog){
-            final XnatResourcecatalog catRes=(XnatResourcecatalog)resource;
-			
-			final CatCatalogBean cat=CatalogUtils.getCatalog(projectPath, catRes);
-			final File catFile=CatalogUtils.getCatalogFile(projectPath, catRes);
-			
-			if(cat!=null){
-				boolean modified=false;
-				
-				if(addUnreferencedFiles){//check for files in the proper resource directory, but not referenced from the existing xml
-					if(CatalogUtils.addUnreferencedFiles(catFile, cat, (UserI)user, now.getEventId())){
-						modified=true;
-					}
-				}
-				
-				if(CatalogUtils.formalizeCatalog(cat, catFile.getParent(), user, now, checksums, removeMissingFiles)){
-					modified=true;
-				}
-				
-				if(modified){
-					CatalogUtils.writeCatalogToFile(cat, catFile,checksums);
-				}
-				
-				// populate (or repopulate) the file stats --- THIS SHOULD BE DONE AFTER modifications to the catalog xml
-	            if (populateStats || modified) {
-	                if (CatalogUtils.populateStats(resource, projectPath) || modified) {
-                        if(resource.save(user, false, false, now)) {
-                            modified = true;
-                        }
-                    }
-	            }
-	            
-	            return modified;
-			}
-		} else if (populateStats) {
-            if (CatalogUtils.populateStats(resource, projectPath)) {
-                return resource.save(user, false, false, now);
-            }
-        }
-		
-		return false;
-	}
-	
-	/**
-	 * Refresh all of the catalogs for a given archivable item.
-	 * @param resourceURI             The URI of the resource to be refreshed.
-	 * @param user                    The user who requested the refresh operation.
-	 * @param details                 Any details about the refresh to be added to the workflow.
-	 * @param checksums               Indicates whether checksums should be calculated for catalog refresh operations.
-	 * @param removeMissingFiles      Indicates whether catalog entries for files that no longer exist should be
-     *                                removed.
-	 * @param addUnreferencedFiles    Indicates whether entries should be added for files that are in the archive but
-     *                                are not already in the catalog.
-	 * @throws ActionException
-	 */
-	public static void refreshResourceCatalog(final ArchiveItemURI resourceURI,final UserI user,final EventDetails details, final boolean populateStats, boolean checksums, boolean removeMissingFiles, boolean addUnreferencedFiles) throws ActionException{
-		try {
-			if(resourceURI instanceof ResourceURII){//if we are referencing a specific catalog, make sure it doesn't actually reference an individual file.
-				if(StringUtils.isNotEmpty(((ResourceURII)resourceURI).getResourceFilePath()) && !((ResourceURII)resourceURI).getResourceFilePath().equals("/")){
-					throw new ClientException(Status.CLIENT_ERROR_BAD_REQUEST,new Exception("This operation cannot be performed directly on a file URL"));
-				}
-			}
-			
-			final ArchivableItem security=resourceURI.getSecurityItem();
-			if(!Permissions.canEdit(user,security)){
-				throw new ClientException(Status.CLIENT_ERROR_FORBIDDEN, new Exception("Unauthorized attempt to add a file to "+ resourceURI.getUri()));
-			}
-			
-			final PersistentWorkflowI wrk = PersistentWorkflowUtils.getOrCreateWorkflowData(null, user, resourceURI.getSecurityItem().getItem(), details);
-			
-			for(XnatAbstractresourceI res:resourceURI.getResources(true)){
-                final String archiveRootPath = resourceURI.getSecurityItem().getArchiveRootPath();
-                refreshResourceCatalog((XnatAbstractresource)res, archiveRootPath, populateStats, checksums, removeMissingFiles, addUnreferencedFiles, user, wrk.buildEvent());
-			}
-			
-			WorkflowUtils.complete(wrk, wrk.buildEvent());
-		} catch (InvalidItemException e) {
-			throw new ServerException(e);
-		} catch (UnknownPrimaryProjectException e) {
-			throw new ServerException(e);
-		} catch (Exception e) {
-			throw new ServerException(e);
-		}
-	}
-}
diff --git a/src/main/java/org/nrg/xnat/utils/XnatUserProvider.java b/src/main/java/org/nrg/xnat/utils/XnatUserProvider.java
index 0314d6fa3d8ffe6aeddb894c61561e9bbbe8b93d..dc484e59d9f49b8c7442df8a4c50efb528b3c26a 100644
--- a/src/main/java/org/nrg/xnat/utils/XnatUserProvider.java
+++ b/src/main/java/org/nrg/xnat/utils/XnatUserProvider.java
@@ -9,8 +9,10 @@
 
 package org.nrg.xnat.utils;
 
+import org.apache.commons.lang3.StringUtils;
 import org.nrg.framework.exceptions.NrgServiceError;
 import org.nrg.framework.exceptions.NrgServiceRuntimeException;
+import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xdat.security.helpers.Users;
 import org.nrg.xdat.security.user.exceptions.UserInitException;
 import org.nrg.xdat.security.user.exceptions.UserNotFoundException;
@@ -26,8 +28,10 @@ import javax.inject.Provider;
  */
 @Component
 public class XnatUserProvider implements Provider<UserI> {
-    public XnatUserProvider(final String login) {
-        _login = login;
+    public XnatUserProvider(final SiteConfigPreferences preferences, final String userKey) {
+        _logger.debug("Initializing user provider with preference key {}", userKey);
+        _preferences = preferences;
+        _userKey = userKey;
     }
 
     /**
@@ -35,16 +39,22 @@ public class XnatUserProvider implements Provider<UserI> {
      */
     @Override
     public UserI get() {
-        if (null == user) {
+        if (null == _user) {
+            final String login = getLogin();
             try {
-                user = Users.getUser(_login);
+                _user = Users.getUser(login);
+                _logger.debug("Retrieved user with login {}", login);
             } catch (UserInitException e) {
-                throw new NrgServiceRuntimeException(NrgServiceError.UserServiceError, "User object for name " + _login + " could not be initialized.");
+                throw new NrgServiceRuntimeException(NrgServiceError.UserServiceError, "User object for name " + login + " could not be initialized.");
             } catch (UserNotFoundException e) {
-                throw new NrgServiceRuntimeException(NrgServiceError.UserNotFoundError, "User with name " + _login + " could not be found.");
+                throw new NrgServiceRuntimeException(NrgServiceError.UserNotFoundError, "User with name " + login + " could not be found.");
             }
         }
-        return user;
+        return _user;
+    }
+
+    public String getUserKey() {
+        return _userKey;
     }
 
     /**
@@ -54,10 +64,21 @@ public class XnatUserProvider implements Provider<UserI> {
      * @return The configured user login name.
      */
     public String getLogin() {
+        if (StringUtils.isBlank(_login)) {
+            _login = _preferences.getValue(_userKey);
+        }
         return _login;
     }
 
+    public void setLogin(final String login) {
+        _login = login;
+    }
+
     private final Logger _logger = LoggerFactory.getLogger(XnatUserProvider.class);
-    private final String _login;
-    private UserI user = null;
+
+    private final SiteConfigPreferences _preferences;
+    private final String                _userKey;
+
+    private String _login;
+    private UserI _user = null;
 }
diff --git a/src/main/resources/META-INF/xnat/preferences/site-config.properties b/src/main/resources/META-INF/xnat/preferences/site-config.properties
deleted file mode 100644
index 21efcef31d97cd90e28e9b642f696878928c6adc..0000000000000000000000000000000000000000
--- a/src/main/resources/META-INF/xnat/preferences/site-config.properties
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# web: site-config.properties
-# XNAT http://www.xnat.org
-# Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
-# All Rights Reserved
-#
-# Released under the Simplified BSD.
-#
-
-# The XNAT siteConfiguration.properties is only used as the default site-wide configuration when a
-# configuration is not already stored in the configuration service. After system initialization, the
-# contents of this file can be used to add new properties to the system, but not to change the values
-# of existing properties. For that, you should only modify site properties using the standard administrative
-# interface.
-checksums=true
-#checksums.property.changed.listener=org.nrg.xnat.utils.ChecksumsSiteConfigurationListener
-#showapplet=false
-enableDicomReceiver=true
-enableDicomReceiver.property.changed.listener=org.nrg.dcm.DicomSCPSiteConfigurationListener
-emailVerification=true
-enableProjectAppletScript=false
-displayNameForGenericImageSession.singular=Session
-displayNameForGenericImageSession.plural=Sessions
-UI.debug-extension-points=false
-UI.allow-advanced-search=true
-UI.allow-new-user-comments=true
-UI.allow-scan-addition=true
-UI.show-left-bar=true
-UI.show-left-bar-projects=true
-UI.show-left-bar-favorites=true
-UI.show-left-bar-search=true
-UI.show-left-bar-browse=true
-UI.show-manage-files=true
-UI.allow-non-admin-project-creation=true
-UI.login_failure_message=Your login attempt failed because the username and password combination you provided was invalid or the site is currently at the maximum number of user sessions. After %d failed login attempts, your user account will be locked. If you believe your account is currently locked, you can:<ul><li>Unlock it by resetting your password</li><li>Wait one hour for it to unlock automatically</li></ul>
-
-# Indicates whether access to the list of system users should be restricted to site administrators only.
-restrictUserListAccessToAdmins=false
-
-UI.allow-blocked-subject-assessor-view=false
-
-files.zip_extensions=zip,jar,rar,ear,gar,mrb
-
-emailAllowNonuserSubscribers=true
-
-requireSaltedPasswords=false
-
-siteDescriptionType=Page
-siteDescriptionPage=/screens/site_description.vm
-siteDescriptionText=SITE DESCRIPTION HERE: Go to Administer -> Configuration -> Site Information to change.
-siteLoginLanding=/screens/QuickSearch.vm
-siteLandingLayout=/Index.vm
-siteHome=/screens/QuickSearch.vm
-siteHomeLayout=/Index.vm
-
-scanTypeMapping=true
-
-passwordExpirationType=Interval
-# Interval, in days, for expiring unchanged passwords (0 disables feature). Uses PostgreSQL interval notation: http://www.postgresql.org/docs/9.0/static/functions-datetime.html
-passwordExpirationInterval=1 year
-# Date for expiring passwords. Generally null by default.
-passwordExpirationDate=
\ No newline at end of file
diff --git a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
index ada35aa23319cb590d414d7754a11861f72a5eb8..f8042115c54b005a382b0538af347c211abdc2ed 100644
--- a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
+++ b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
@@ -135,6 +135,16 @@ siteUrl:
         come to the site). localhost only works if the web browser is located on the same machine.
         You are required to guarantee that this address is functional for reaching the site.
 
+processingUrl:
+    kind: panel.input.text
+    name: processingUrl
+    label: Processing Url
+    validation: url onblur
+    description: >
+        The address to be used for processing. If this is left blank, pipelines will use the site
+        URL. If your pipelines need to connect to an internal URL, different from your external
+        site URL, provide one here.
+
 adminEmail:
     kind: panel.input.email
     name: adminEmail
@@ -145,6 +155,16 @@ adminEmail:
         on system events, such as errors, processing completion, new user registration and so on. These emails
         can be configured on the Notifications tab.
 
+primaryAdminUsername:
+    kind: panel.input.text
+    name: primaryAdminUsername
+    label: Primary Admin Username
+    validation: id-strict max-length:40 onblur
+    placeholder: User account to use when performing system functions
+    description: >
+        This should be the login name of an enabled and valid user. The specified user must be a site
+        administrator.
+
 siteWideAlertStatus:
     kind: panel.select.single
     name: siteWideAlertStatus
@@ -259,6 +279,7 @@ siteInfo:
     contents:
         ${siteId}
         ${siteUrl}
+        ${processingUrl}
         ${siteDescriptionType}
         ${siteLoginLanding}
 #        ${siteLandingLayout}
@@ -274,6 +295,7 @@ adminInfo:
     url: /xapi/siteConfig
     contents:
         ${adminEmail}
+        ${primaryAdminUsername}
 
 siteWideAlerts:
     kind: panel.form
@@ -297,7 +319,7 @@ generalSecuritySettings:
     contents:
         securityChannel:
             kind: panel.select.single
-            name: ":security.channel"
+            name: ":securityChannel"
             label: Security Channel
             options:
                 any: Any
@@ -321,7 +343,7 @@ generalSecuritySettings:
             description: "Should this site restrict access to the list of system users to site administrators only? If turned on, the site is more secure, but this restricts project owners from being able to administer users in their projects directly."
         uiAllowNonAdminProjectCreation:
             kind: panel.input.switchbox
-            name: ":UI.allow-non-admin-project-creation"
+            name: ":uiAllowNonAdminProjectCreation"
             label: "Allow non-administrators <br>to create projects?"
             onText: Allow
             offText: Do Not Allow
@@ -373,17 +395,17 @@ userLoginsSessionControls:
 #            description: Allow user to resume where they left off, if logging back in after a session timeout?
         maximumConcurrentSessions:
             kind: panel.input.number
-            name: ":sessions.concurrent_max"
+            name: ":concurrentMaxSessions"
             label: Maximum Concurrent Sessions
             validation: integer gte:1 onblur
             description: The maximum number of permitted sessions a user can have open simultaneously. You must restart Tomcat for changes to this to take effect.
         loginFailureMessage:
             kind: panel.textarea
-            name: ":UI.login_failure_message"
+            name: ":uiLoginFailureMessage"
             label: Login Failure Message
             code: html
             description: Text to show when a user fails to login
-#            value: "?? XNAT:data:siteConfig:UI.login_failure_message"
+#            value: "?? XNAT:data:siteConfig:uiLoginFailureMessage"
         maximumFailedLogins:
             kind: panel.input.number
             name: maxFailedLogins
@@ -548,25 +570,25 @@ securityServices:
         securityServicesFeatureDefault:
             kind: panel.input.text
 #            id: securityServicesFeatureDefault
-            name: ":security.services.feature.default"
+            name: ":featureService"
             label: Feature Default
             size: 50
         securityServicesFeatureRepoDefault:
             kind: panel.input.text
 #            id: securityServicesFeatureRepoDefault
-            name: ":security.services.featureRepository.default"
+            name: ":featureRepositoryService"
             label: Feature Repository Default
             size: 50
         securityServicesRoleDefault:
             kind: panel.input.text
 #            id: securityServicesRoleDefault
-            name: ":security.services.role.default"
+            name: ":roleService"
             label: Role Default
             size: 50
         securityServicesRoleRepositoryDefault:
             kind: panel.input.text
 #            id: securityServicesRoleRepositoryDefault
-            name: ":security.services.roleRepository.default"
+            name: ":roleRepositoryService"
             label: Role Repository Default
             size: 50
 
@@ -580,33 +602,33 @@ emailServerSettings:
     contents:
         smtpEnabled:
             kind: panel.input.switchbox
-            name: ":smtp.enabled"
+            name: ":smtpEnabled"
             label: "Enable SMTP?"
             onText: Enabled
             offText: Disabled
         hostname:
             kind: panel.input.text
-            name: host
+            name: smtpHostname
             label: Host
             placeholder: localhost
         port:
             kind: panel.input.number
-            name: port
+            name: smtpPort
             label: Port
             validation: allow-empty integer onblur
             placeholder: 587
         username:
             kind: panel.input.text
-            name: username
+            name: smtpUsername
             label: Username
             placeholder: name@host.org
         password:
             kind: panel.input.password
-            name: password
+            name: smtpPassword
             label: Password
         protocol:
             kind: panel.input.text
-            name: protocol
+            name: smtpProtocol
             label: Protocol
             placeholder: smtp
         # subhead breaks up panel items
@@ -616,19 +638,19 @@ emailServerSettings:
             label: Properties
         smtpAuth:
             kind: panel.input.switchbox
-            name: ":mail.smtp.auth"
+            name: ":smtpAuth"
             label: SMTP Auth?
             onText: Enabled
             offText: Disabled
         smtpStartTls:
             kind: panel.input.switchbox
-            name: ":mail.smtp.starttls.enable"
+            name: ":smtpStartTls"
             label: Start TLS?
             onText: Enabled
             offText: Disabled
-        smtpSSLTrust:
+        smtpSslTrust:
             kind: panel.input.text
-            name: ":mail.smtp.ssl.trust"
+            name: ":smtpSslTrust"
             label: SSL Trust
             placeholder: localhost
         emailPrefix:
@@ -989,7 +1011,7 @@ registrationOptions:
                 when a user registers.
         uiAllowNewUserComments:
             kind: panel.input.switchbox
-            name: ":UI.allow-new-user-comments"
+            name: ":uiAllowNewUserComments"
             label: "Allow User Comments <br>on Registration?"
             onText: Allow
             offText: Do Not Allow
@@ -1004,11 +1026,11 @@ manageDataTypes:
     contents:
         displayNameForGenericImageSessionSingular:
             kind: panel.input.text
-            name: ":displayNameForGenericImageSession.singular"
+            name: ":imageSessionDisplayNameSingular"
             label: "Singular Display Name For Generic Image Session Singular"
         displayNameForGenericImageSessionPlural:
             kind: panel.input.text
-            name: ":displayNameForGenericImageSession.plural"
+            name: ":imageSessionDisplayNamePlural"
             label: "Plural Display Name For Generic Image Session Singular"
 
 sessionBuilder:
@@ -1019,6 +1041,15 @@ sessionBuilder:
     contentType: json
     url: /xapi/siteConfig
     contents:
+        receivedFileUser:
+            kind: panel.input.text
+            name: receivedFileUser
+            label: Received File User
+            validation: id-strict max-length:40 onblur
+            placeholder: User account for performing import and archiving operations
+            description: >
+                This should be the login name of an enabled and valid user. The specified user must be a site
+                administrator.
         sessionXmlRebuilderRepeat:
             kind: panel.input.number
             name: sessionXmlRebuilderRepeat
@@ -1333,7 +1364,6 @@ misc:
     kind: panel.form
     name: misc
     label: Miscellaneous
-    footer: false
     method: POST
     contentType: json
     url: /xapi/siteConfig
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
index 66a1459dd668e05dc316b78b65aa98b80805d744..fe6a20130ab00d352b96bf814de8de6e32099ff2 100644
--- a/src/main/resources/log4j.properties
+++ b/src/main/resources/log4j.properties
@@ -39,9 +39,7 @@ log4j.additivity.org.nrg.schedule=false
 #
 log4j.category.tasks=WARN, tasks
 log4j.additivity.tasks=false
-log4j.category.org.nrg.xnat.initialization.InitializingTasksExecutor=INFO, tasks
-log4j.additivity.org.nrg.xnat.initialization.InitializingTasksExecutor=false
-log4j.category.org.nrg.xnat.initialization.tasks=WARN, tasks
+log4j.category.org.nrg.xnat.initialization.tasks=INFO, tasks
 log4j.additivity.org.nrg.xnat.initialization.tasks=false
 
 #
diff --git a/src/main/webapp/WEB-INF/conf/services.properties b/src/main/webapp/WEB-INF/conf/services.properties
deleted file mode 100644
index 429b50d9c5cb5010a892341c71e723face3bb53e..0000000000000000000000000000000000000000
--- a/src/main/webapp/WEB-INF/conf/services.properties
+++ /dev/null
@@ -1,91 +0,0 @@
-#
-# D:/Development/XNAT/1.6/xnat_builder_1_6dev/plugin-resources/conf/services.properties
-# XNAT http://www.xnat.org
-# Copyright (c) 2014, Washington University School of Medicine
-# All Rights Reserved
-#
-# Released under the Simplified BSD.
-#
-# Last modified 2/7/14 12:19 PM
-#
-site.title=XNAT
-
-datasource.name=xnat
-datasource.driver=org.postgresql.Driver
-datasource.url=jdbc:postgresql://localhost/xnat
-datasource.username=xnat
-datasource.password=xnat
-
-hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
-hibernate.hbm2ddl.auto=update
-hibernate.show_sql=false
-hibernate.cache.use_second_level_cache=true
-hibernate.cache.use_query_cache=true
-
-mailserver.host=mail.server
-mailserver.port=25
-mailserver.username=
-mailserver.password=
-mailserver.protocol=smtp
-mailserver.admin=jrherrick@wustl.edu
-mailserver.prefix=XNAT
-
-# Session XML rebuilder settings. interval is in minutes, repeat is in milliseconds.
-services.rebuilder.interval=5
-services.rebuilder.repeat=60000
-
-# Settings for the DICOM SCP Receiver. You can change the AE title and port here, as well as change the user that is
-# used for operations on received files, e.g. archiving sessions in projects with auto-archiving.
-services.dicom.scp.aetitle=XNAT
-services.dicom.scp.port=8104
-services.dicom.scp.receivedfileuser=admin
-
-# Indicate the required security channel for the server. This can be http, https, or any.
-security.channel=any
-
-# Number of failed login attempts before accounts are temporarily locked (-1 disables feature).
-security.max_failed_logins=-1
-# Number of milliseconds to lock user accounts that have exceeded the max_failed_logins count (3600000 for 1 hour, 86400000 for 24 hours).
-security.max_failed_logins_lockout_duration=86400000
-# Number of seconds of inactivity before an account is disabled (31556926 for 1 year).
-security.inactivity_before_lockout=31556926
-
-# Interval for timing out alias tokens. Uses PostgreSQL interval notation: http://www.postgresql.org/docs/9.0/static/functions-datetime.html
-security.token_timeout=2 days
-
-# Java regular expression that new passwords must match. For no complexity checks, set this to ^.*$
-security.password_complexity=^.*$
-# Message that explains the password complexity requirements. This is displayed when user chooses a new password that fails to meet them.
-security.password_complexity_message=Password is not sufficiently complex.
-
-# Number of days for which a password cannot be reused.
-security.password_history=365
-
-# The maximum number of permitted sessions a user can have open simultaneously.
-security.sessions.concurrent_max=1000
-
-# The following parameters are used to allow/force users to enter change justifications when modifying data.
-audit.require_change_justification=false
-audit.show_change_justification=false
-
-# Sets default values for the ActiveMQ configuration.
-amq.usage.temp=128mb
-amq.usage.mem=512mb
-amq.usage.store=1gb
-
-# Comma-separated list of the providers that users will be able to use to authenticate.
-provider.providers.enabled=db
-
-provider.db.name=Database
-provider.db.id=localdb
-provider.db.type=db
-
-# Add "ldap1" to the enabled provider list above and fill in the missing fields to enable LDAP authentication.
-provider.ldap1.name=LDAP
-provider.ldap1.id=ldap1
-provider.ldap1.type=ldap
-provider.ldap1.address=
-provider.ldap1.userdn=
-provider.ldap1.password=
-provider.ldap1.search.base=
-provider.ldap1.search.filter=
diff --git a/src/main/webapp/page/admin/plugins/content.jsp b/src/main/webapp/page/admin/plugins/content.jsp
new file mode 100755
index 0000000000000000000000000000000000000000..c9e1fac5839dac0f1a2f95d0dfd2769aeeb2b0b4
--- /dev/null
+++ b/src/main/webapp/page/admin/plugins/content.jsp
@@ -0,0 +1,103 @@
+<%@ page session="true" contentType="text/html" pageEncoding="UTF-8" language="java" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="pg" tagdir="/WEB-INF/tags/page" %>
+
+<%--
+  ~ web: content.jsp
+  ~ XNAT http://www.xnat.org
+  ~ Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+  ~ All Rights Reserved
+  ~
+  ~ Released under the Simplified BSD.
+  --%>
+
+<c:set var="redirect">
+    <div class="error">Not authorized. Redirecting...</div>
+    <script> window.location.href = XNAT.url.rootUrl('/') </script>
+</c:set>
+
+<pg:restricted msg="${redirect}">
+
+    <div id="page-body">
+        <div class="pad">
+
+            <div id="admin-page">
+                <header id="content-header">
+                    <h2 class="pull-left">Plugin Settings</h2>
+                    <div class="clearfix"></div>
+                </header>
+
+                <!-- Plugin Settings tab container -->
+                <div id="plugin-settings-tabs">
+
+                    <div class="content-tabs xnat-tab-container">
+
+                        <%--
+                        <div class="xnat-nav-tabs side pull-left">
+                            <!-- ================== -->
+                            <!-- Admin tab flippers -->
+                            <!-- ================== -->
+                        </div>
+                        <div class="xnat-tab-content side pull-right">
+                            <!-- ================== -->
+                            <!-- Admin tab panes    -->
+                            <!-- ================== -->
+                        </div>
+                        --%>
+
+                    </div>
+
+                </div>
+
+                <c:import url="/xapi/plugins" var="plugins"/>
+
+                <script>
+                    (function(){
+
+                        XNAT.xapi = getObject(XNAT.xapi);
+
+                        <c:if test="${not empty plugins}">
+                            XNAT.xapi.plugins = ${plugins};
+                            XNAT.data['/xapi/plugins'] = XNAT.xapi.plugins;
+                        </c:if>
+
+                        XNAT.data = extend(true, {
+                            plugins: XNAT.xapi.plugins
+                        }, XNAT.data||{});
+
+                        // these properties MUST be set before spawning 'tabs' widgets
+                        XNAT.tabs.container = $('#plugin-settings-tabs').find('div.content-tabs');
+                        XNAT.tabs.layout = 'left';
+
+                        function pluginTabs(name){
+                            var tabSpawn = XNAT.spawner.resolve(name + '/siteSettings');
+                            tabSpawn.render(XNAT.tabs.container, 200)
+                                    .fail(function(){
+                                        console.log('"' + name + '" site settings tabs not found');
+                                    });
+                            return tabSpawn;
+                        }
+
+                        // try to get plugin tab Spawner objects
+                        // from url:
+                        // /xapi/spawner/resolve/pluginName/siteSettings
+                        forOwn(XNAT.xapi.plugins, function(name, obj){
+                            // first try to load admin js file at
+                            // /scripts/xnat-plugins/pluginName/admin.js
+                            //loadjs(XNAT.url.rootUrl('/scripts/xnat-plugins/' + name + '/admin.js'), name);
+                            pluginTabs(name);
+                        });
+
+                    })();
+                </script>
+
+            </div>
+
+        </div>
+    </div>
+    <!-- /#page-body -->
+
+    <div id="xnat-scripts"></div>
+
+</pg:restricted>
+
diff --git a/src/main/webapp/page/admin/plugins/index.jsp b/src/main/webapp/page/admin/plugins/index.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..4ed945e8cbf7840adf529fd69954edf71a4debd4
--- /dev/null
+++ b/src/main/webapp/page/admin/plugins/index.jsp
@@ -0,0 +1,20 @@
+<%@ page session="true" contentType="text/html" pageEncoding="UTF-8" language="java" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="pg" tagdir="/WEB-INF/tags/page" %>
+
+<%--
+  ~ web: index.jsp
+  ~ XNAT http://www.xnat.org
+  ~ Copyright (c) 2016, Washington University School of Medicine and Howard Hughes Medical Institute
+  ~ All Rights Reserved
+  ~
+  ~ Released under the Simplified BSD.
+  --%>
+
+<pg:wrapper>
+    <pg:xnat>
+
+        <jsp:include page="content.jsp"/>
+
+    </pg:xnat>
+</pg:wrapper>
diff --git a/src/main/webapp/page/content.jsp b/src/main/webapp/page/content.jsp
index 9994de235c39995e68bad10c44bc872c46b2ff26..d60e848cf43461941d940bf66714a213c4cade5c 100755
--- a/src/main/webapp/page/content.jsp
+++ b/src/main/webapp/page/content.jsp
@@ -21,7 +21,7 @@
 
 <div id="page-wrapper">
     <div class="pad">
-        <div id="page-content">loading...</div>
+        <div id="page-content"></div>
     </div>
 </div>
 
@@ -29,13 +29,13 @@
     (function(){
 
         var customPage = XNAT.app.customPage;
-        var $pageContent = $('#page-content');
+        var $pageContent = $('#page-content').html('loading...');
 
-        customPage.getPage(['', '/#'], $pageContent);
+        customPage.getPage(null, $pageContent);
 
-//        window.onhashchange = function(){
-//            customPage.getPage('', $pageContent);
-//        }
+        //        window.onhashchange = function(){
+        //            customPage.getPage('', $pageContent);
+        //        }
 
     })();
 </script>
diff --git a/src/main/webapp/resources/samples/prefs-init.ini b/src/main/webapp/resources/samples/prefs-init.ini
new file mode 100644
index 0000000000000000000000000000000000000000..333765e7c1141af6da7ac369d9a3d6924f516901
--- /dev/null
+++ b/src/main/webapp/resources/samples/prefs-init.ini
@@ -0,0 +1,30 @@
+#
+# prefs-init.ini
+# XNAT http://www.xnat.org
+# Copyright (c) 2016, Washington University School of Medicine
+# All Rights Reserved
+#
+# Released under the Simplified BSD.
+#
+
+[siteConfig]
+siteUrl=http://xnat-centos.xnat.org
+adminEmail=jrherrick@wustl.edu
+archivePath=/data/xnat/archive
+buildPath=/data/xnat/build
+cachePath=/data/xnat/cache
+ftpPath=/data/xnat/ftp
+pipelinePath=/data/xnat/pipeline
+prearchivePath=/data/xnat/prearchive
+initialized=true
+
+[notifications]
+hostname=smtp.gmail.com
+username=foo@gmail.com
+password=password1234
+port=587
+protocol=smtp
+smtpAuth=true
+smtpStartTls=true
+
+
diff --git a/src/main/webapp/WEB-INF/conf/xnat-conf.properties b/src/main/webapp/resources/samples/xnat-conf.properties
similarity index 100%
rename from src/main/webapp/WEB-INF/conf/xnat-conf.properties
rename to src/main/webapp/resources/samples/xnat-conf.properties
diff --git a/src/main/webapp/scripts/globals.js b/src/main/webapp/scripts/globals.js
index 4301f3c2cf891cd8981b130f05186c326eab2c76..f5748d4c99b7f0b0f3e2923af1accd999404867c 100644
--- a/src/main/webapp/scripts/globals.js
+++ b/src/main/webapp/scripts/globals.js
@@ -1209,7 +1209,7 @@ function writeCSS( href ){
 function loadCSS( url, parent ){
     // use CSS-style selector for 'parent'
     parent = parent ? document.querySelector(parent) : document.querySelector('head');
-    parent.appendChild(scriptElement(url, min, name));
+    parent.appendChild(cssElement(url));
 }
 
 // basic indentation formatting only
@@ -1243,8 +1243,14 @@ function prettifyJSON(data, indent) {
 // call 'test' at 'interval' until it returns true
 // then execute 'callback' -- basic but useful
 function waitForIt(interval, test, callback){
+    var _test = test;
+    if (typeof test !== 'function') {
+        _test = function(){
+            return !!test
+        }
+    }
     var waiting = setInterval(function(){
-        if (test()) {
+        if (_test()) {
             var called = callback();
             clearInterval(waiting);
             return called;
diff --git a/src/main/webapp/scripts/project/projResourceMgmt.js b/src/main/webapp/scripts/project/projResourceMgmt.js
index 70944351714414799552727e93954cc627854ff4..5b7caf0b2590d3472795d6070000f5ae75152a2f 100644
--- a/src/main/webapp/scripts/project/projResourceMgmt.js
+++ b/src/main/webapp/scripts/project/projResourceMgmt.js
@@ -181,7 +181,7 @@ XNAT.app.pResources={
 							dataType: 'json'
 						});
 						getUploadConfigAjax.done( function( data, textStatus, jqXHR ) {
-							if (typeof(data)!=undefined && $.isArray(data) && data.length>0) {
+							if (typeof(data)!=undefined && $.isArray(data)) {
 								var uploaderConfig = data; 
 								var alreadyConfig = false;
 								var NEW_HANDLER = "Uploaded " + props.name;
diff --git a/src/main/webapp/scripts/project/settingsTabMgmt.js b/src/main/webapp/scripts/project/settingsTabMgmt.js
index c13ccc0a0a8ae819ba613f228a03374586d26408..bbc6bd69b227c76601e2b068caeb418242a3ce12 100644
--- a/src/main/webapp/scripts/project/settingsTabMgmt.js
+++ b/src/main/webapp/scripts/project/settingsTabMgmt.js
@@ -60,8 +60,8 @@ function fullConfigHandler() {
     };
 
     var data = buildSettingsUpdateRequestBody([
-        'siteId','UI.debug-extension-points', 'siteDescriptionType', 'siteDescriptionText', 'siteDescriptionPage', 'siteUrl', 'siteAdminEmail', 'siteLoginLanding', 'siteLandingLayout', 'siteHome', 'siteHomeLayout'
-        , 'enableCsrfToken', 'enableCsrfEmail', 'restrictUserListAccessToAdmins', 'UI.allow-non-admin-project-creation', 'requireSaltedPasswords', 'passwordExpirationType', 'passwordExpirationInterval', 'passwordExpirationDate'
+        'siteId','uiDebugExtensionPoints', 'siteDescriptionType', 'siteDescriptionText', 'siteDescriptionPage', 'siteUrl', 'siteAdminEmail', 'siteLoginLanding', 'siteLandingLayout', 'siteHome', 'siteHomeLayout'
+        , 'enableCsrfToken', 'enableCsrfEmail', 'restrictUserListAccessToAdmins', 'uiAllowNonAdminProjectCreation', 'requireSaltedPasswords', 'passwordExpirationType', 'passwordExpirationInterval', 'passwordExpirationDate'
         , 'archivePath', 'checksums', 'prearchivePath', 'cachePath', 'ftpPath', 'buildPath', 'pipelinePath'
         , 'requireLogin', 'enableNewRegistrations', 'emailVerification'
         , 'error', 'issue', 'newUser', 'update', 'emailAllowNonuserSubscribers', 'smt.enabled'
diff --git a/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js b/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js
index 46e1d2fc332b4dba728a13a711fb407a205d3b94..64709426d6baa2e3bd371e1af1958b521ec1a5d4 100644
--- a/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js
+++ b/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js
@@ -21,17 +21,34 @@ if(typeof XNAT.app.abu.abuConfigs === 'undefined'){
 	 		});
 		},
 		modalOpts:{
-			width: 840,  
-			height: 580,  
-			id: 'xmodal-abu',  
+			width: 840,
+			height: 580,
+			id: 'xmodal-abu',
 			title: "Automation-Based Launcher/Uploader",
 			content: "<div id='modalUploadDiv'></div>",
-			ok: 'hide',
-			okLabel: 'Done',
-			okAction: function(){ },
-			cancel: 'hide',
-			cancelLabel: 'Cancel',
-			closeBtn: 'hide'
+			closeBtn: "hide",
+			buttons: {
+				done: {
+					label: 'OK',
+					isDefault: true,
+					close: true,
+					action: function() {}
+				},
+				process: {
+					label: 'Process Uploaded Data',
+					isDefault: true,
+					close: false,
+					action: function() {}
+				},
+				cancel: {
+					label: 'Cancel',
+					close: true
+				}
+			},
+			beforeShow: function(obj) {
+				obj.$modal.find('#xmodal-abu-process-button').prop('disabled','disabled');
+				obj.$modal.find('#xmodal-abu-done-button').hide();
+			}
 		},
 
 		// Much of the remainder of the options code originates from ConfiguredResourceUploader.js
@@ -287,14 +304,15 @@ XNAT.app.abu.hasContextEvents = function(usage,dataType){
 
 XNAT.app.abu.eventHandlerChange = function(){
 	var eventHandler = $('#eventHandlerSelect').val();
+	XNAT.app.abu.updateOptionsCheckboxes(eventHandler);
 	if (typeof eventHandler === 'undefined' || eventHandler == null || eventHandler.length<1 && ($("#handlerDefaultOption").html() == 'NONE DEFINED' || $("#handlerDefaultOption").html() == 'SELECT')) {
-		$("#abu-process-button").addClass("abu-button-disabled");
+		$("#xmodal-abu-process-button").prop("disabled","disabled");
 		//$("#abu-process-button-text").html("&nbsp;");
-		$("#abu-process-button").css("visibility","hidden");
+		//$("#abu-process-button").css("visibility","hidden");
 	} else if ((XNAT.app.abu.usageSelect=='Launch') || (abu._fileUploader._uploadStarted && abu._fileUploader._filesInProgress<1)) {
-		$("#abu-process-button").removeClass("abu-button-disabled");
+		$("#xmodal-abu-process-button").prop("disabled",false);
 		//$("#abu-process-button-text").html("&nbsp;");
-		$("#abu-process-button").css("visibility","visible");
+		//$("#abu-process-button").css("visibility","visible");
 	}
 	XNAT.app.abu.filesProcessed = false;
 }
@@ -422,7 +440,7 @@ XNAT.app.abu.populateEventHandlerSelect = function(){
 					}
 					if (doAssign) {
 						$('#eventHandlerSelect').append('<option value="' + currEvent.triggerId + '" class="' + currEvent.scope + '">' +
-						 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.scriptId) + '</option>');
+						 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.event) + '</option>');
 					}
 					continue outerLoop;
 				}
@@ -480,7 +498,7 @@ XNAT.app.abu.populateWhatToDoSelect = function(){
  											? (resourceConfigs[h].name + " --> ") : ""
 										) +
 
-									 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.scriptId) + '</option>');
+									 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.event) + '</option>');
 									resourceMatch = true;
 								}
 								continue outerLoop;
@@ -518,7 +536,7 @@ XNAT.app.abu.populateWhatToDoSelect = function(){
 					}
 					if (doAssign) {
 						$('#whatToDoSelect').append('<option value="resource-' + XNAT.app.abu.cacheConstant + ':launch-' + currEvent.triggerId + '" class="' + currEvent.scope + '">' + /*"Upload --> " +*/ 
-						 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.scriptId) + '</option>');
+						 ((typeof(currEvent.description) !== 'undefined' && currEvent.description.length>0) ? currEvent.description : currEvent.event) + '</option>');
 					}
 					continue outerLoop;
 				}
@@ -549,7 +567,7 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 	var props = $(XNAT.app.abu.currentLink).attr("data-props");
 	XNAT.app.abu.contextResourceConfigs = XNAT.app.abu.abuConfigs.getAllConfigsByType(type,props);		
 	var resourceConfigs = XNAT.app.abu.contextResourceConfigs;
-	var scriptDiv = "<div class='abu-xnat-interactivity-area-contents'>";
+	var scriptDiv = '';
 	var usageSelect = '<div class="abu-xnat-interactivity-area-sub usage-area"><span class="interactivityAreaSpan">Usage:</span> <select id="usageSelect" onchange="XNAT.app.abu.usageSelectAction()">'; 
                 if (typeof usageType == 'undefined' || usageType == 'upload') {
 			XNAT.app.abu.usageSelect = 'Upload';
@@ -586,7 +604,6 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 	scriptDiv+=resourceSelect;
 	scriptDiv+=eventSelect;
 	scriptDiv+=whatToDoSelect;
-	scriptDiv+='</div>';
 	try {
 		xmodal.open(XNAT.app.abu.abuConfigs.modalOpts);
 		abu.initializeUploader({
@@ -619,7 +636,7 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 					// Force press of processing button after upload is started so workflow is created.  Only want one workflow per upload.
 					// Don't want cancellation of process during upload or incomplete file could end up in resource
 					if (abu._fileUploader._currentAction.indexOf("import-handler=" + XNAT.app.abu.importHandler)<0) {
-						$("#abu-done-button").addClass("abu-button-disabled");
+						$("#xmodal-abu-done-button").hide();
 					}
 					$("#resourceSelect").prop('disabled','disabled');
 					if ($("#whatToDoSelect").val() != "") {
@@ -631,22 +648,31 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 					var eventHandler = $('#eventHandlerSelect').val();
 					if (typeof eventHandler !== 'undefined' && eventHandler != null && eventHandler.length>0) {
 						if ($(".abu-upload-complete-text").length==0) {
-							$("#abu-done-button").removeClass("abu-button-disabled");
+							// $("#abu-done-button").removeClass("abu-button-disabled");
+							$("#abu-done-button").show();
+							$('#xmodal-abu-process-button').hide();
 						} else {
-							$("#abu-done-button").addClass("abu-button-disabled");
+							// $("#abu-done-button").addClass("abu-button-disabled");
+							$("#abu-done-button").hide();
+							$("#xmodal-abu-cancel-button").hide();
 						}
-						$("#abu-process-button").removeClass("abu-button-disabled");
-						$("#abu-process-button-text").html("Process Files");
-						$("#abu-process-button").css("visibility","visible");
+						$("#xmodal-abu-process-button").prop("disabled",false);
+						$("#xmodal-abu-process-button").show();
+						//$("#abu-process-button-text").html("Process Files");
+						//$("#abu-process-button").css("visibility","visible");
 					} else {
-						$("#abu-done-button-text").html("Done");
-						$("#abu-done-button-text").addClass("abu-done-button-done");
-						$("#abu-done-button-text").removeClass("abu-done-button-cancel");
-						$("#abu-done-button").removeClass("abu-button-disabled");
+						// $("#abu-done-button-text").html("Done");
+						// $("#abu-done-button-text").addClass("abu-done-button-done");
+						// $("#abu-done-button-text").removeClass("abu-done-button-cancel");
+						// $("#abu-done-button").removeClass("abu-button-disabled");
+						$("#xmodal-abu-cancel-button").hide();
+						$('#xmodal-abu-done-button').show();
+						$('#xmodal-abu-process-button').hide();
 					}
 				},
 			processFunction:function(){
 					XNAT.app.abu.processFiles();
+					$('#xmodal-abu-process-button').hide();
 				},
 			showEmailOption:true,
 			showCloseOption:true,
@@ -655,7 +681,7 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 			showUpdateOption:false,
 		});	
 		abu._fileUploader.buildUploaderDiv();
-		$(".abu-xnat-interactivity-area").html(scriptDiv);
+		$(".abu-xnat-interactivity-area-contents").html(scriptDiv);
 		XNAT.app.abu.populateEventHandlerSelect();
 		abu._fileUploader._currentAction = XNAT.app.abu.automationHandlerUrl(true);
 		if (typeof usageType !== 'undefined' && usageType != null) {
@@ -663,43 +689,49 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){
 			if (usageType === 'launch') {
 				var eventHandler = $('#eventHandlerSelect').val();
 				if (eventHandler != undefined && eventHandler != null && eventHandler.length>0) {
-					$("#abu-process-button").removeClass("abu-button-disabled");
-					$("#abu-process-button").css("visibility","visible");
+					$("#xmodal-abu-process-button").prop("disabled",false);
+					$("#xmodal-abu-process-button").show();
 				} else {
-					$("#abu-done-button").removeClass("abu-button-disabled");
-					$("#abu-done-button-text").html("Done");
-					$("#abu-done-button-text").addClass("abu-done-button-done");
-					$("#abu-done-button-text").removeClass("abu-done-button-cancel");
+					// $("#abu-done-button").removeClass("abu-button-disabled");
+					// $("#abu-done-button-text").html("Done");
+					// $("#abu-done-button-text").addClass("abu-done-button-done");
+					// $("#abu-done-button-text").removeClass("abu-done-button-cancel");
+					$("#xmodal-abu-process-button").prop("disabled",false);
+					$('#xmodal-abu-done-button').show();
 				}
-				$(".upload-area").css("display","none");
-				$(".whattodo-area").css("display","none");
-				$("#abu-upload-button").addClass("abu-button-disabled");
+				$(".upload-area").hide();
+				$(".whattodo-area").hide();
+				$(".abu-upload-button")
+					.hide()
+					.prop("disabled","disabled");
 				abu._fileUploader.DRAG_AND_DROP_ON = false;
-				$("#abu-upload-button").css("display","none");
-				$("#abu-process-button-text").html("Run script");
-				$("#abu-done-button-text").html("Cancel");
-				$("#abu-done-button-text").addClass("abu-done-button-cancel");
-				$("#abu-done-button-text").removeClass("abu-done-button-done");
+				$("#xmodal-abu-process-button").html("Run script");
+				// $("#abu-done-button-text").html("Cancel");
+				// $("#abu-done-button-text").addClass("abu-done-button-cancel");
+				// $("#abu-done-button-text").removeClass("abu-done-button-done");
+				$("#xmodal-abu-done-button").hide();
+				$("#xmodal-abu-process-button").show();
 				if ($('#eventHandlerSelect option').size()>1 && $('#eventHandlerSelect').val()=="") {
-					$("#abu-process-button").addClass("abu-button-disabled");
-					//$("#abu-process-button-text").html("&nbsp;");
-					$("#abu-process-button").css("visibility","hidden");
+					$("#xmodal-abu-process-button").prop("disabled","disabled");
+					// $("#abu-process-button-text").html("&nbsp;");
+					// $("#xmodal-abu-process-button").hide();
 				} 
 				$("#file-uploader-instructions-sel").css("display","none");
 			} else {
 				XNAT.app.abu.populateWhatToDoSelect();
 				if ($('#whatToDoSelect option').size()>1 && $('#whatToDoSelect').val()=="") {
+					$("#abu-upload-button").prop("disabled","disabled");
 					$("#abu-upload-button").addClass("abu-button-disabled");
 					abu._fileUploader.DRAG_AND_DROP_ON = false;
 				} 
-				$("#abu-process-button").addClass("abu-button-disabled");
+				$("#xmodal-abu-process-button").prop("disabled","disabled");
 				//$("#abu-process-button-text").html("&nbsp;");
-				$("#abu-process-button").css("visibility","hidden");
-				$(".upload-area").css("display","none");
-				$(".eventhandler-area").css("display","none");
+				// $("#xmodal-abu-process-button").hide();
+				$(".upload-area").hide();
+				$(".eventhandler-area").hide();
 			}
 		} else {
-			$(".whattodo-area").css("display","none");
+			$(".whattodo-area").hide();
 		}
 	} catch(e) {
 		console.log(e.stack);
@@ -716,27 +748,33 @@ XNAT.app.abu.usageSelectAction = function(){
 	XNAT.app.abu.usageSelect = $('#usageSelect').val();
 	if (XNAT.app.abu.usageSelect=='Upload') {
 		XNAT.app.abu.populateEventHandlerSelect();
-		$("#abu-done-button").removeClass("abu-button-disabled");
+		$("#xmodal-abu-done-button")
+			.show()
+			.prop("disabled",false);
+		$(".abu-upload-button").prop("disabled",false);
 		$("#abu-upload-button").removeClass("abu-button-disabled");
 		abu._fileUploader.DRAG_AND_DROP_ON = true;
-		$("#abu-process-button").addClass("abu-button-disabled");
+		$("#xmodal-abu-process-button")
+			.hide()
+			.prop("disabled","disabled");
 		//$("#abu-process-button-text").html("&nbsp;");
-		$("#abu-process-button").css("visibility","hidden");
 		$("#script-select-text").html("Post-upload processing script:");
 		$("#resourceSelect").prop('disabled',false);
 		$(".response_text").html('');
 	} else if (XNAT.app.abu.usageSelect=='Launch') { 
 		XNAT.app.abu.populateEventHandlerSelect();
-		$("#abu-done-button").removeClass("abu-button-disabled");
+		$(".abu-upload-button").prop("disabled","disabled");
 		$("#abu-upload-button").addClass("abu-button-disabled");
 		abu._fileUploader.DRAG_AND_DROP_ON = false;
 		var eventHandler = $('#eventHandlerSelect').val();
 		if (eventHandler != undefined && eventHandler != null && eventHandler.length>0) {
-			$("#abu-process-button").removeClass("abu-button-disabled");
-			$("#abu-process-button").css("visibility","visible");
+			$("#xmodal-abu-process-button").prop("disabled",false);
+			// $("#xmodal-abu-process-button").show();
 		}
 		$("#script-select-text").html("Script to launch:");
-		$("#abu-process-button-text").html("Run script");
+		$("#xmodal-abu-process-button").html("Run script");
+		$("#xmodal-abu-done-button").hide();
+		$("#xmodal-abu-process-button").show();
 		$("#resourceSelect").prop('disabled','disabled');
 		$(".response_text").html('');
 	}
@@ -766,6 +804,85 @@ XNAT.app.abu.updateModalAction = function(){
 	XNAT.app.abu.populateEventHandlerSelect();
 }
 
+XNAT.app.abu.updateOptionsCheckboxes = function(selectVal) {
+	// First set default values that will take effect if there is no event handlers (e.g. configured resource uploads with no scripts);
+	$("#extractRequestBoxDiv").show();
+	$("#extractRequestBox").attr('checked',true);
+	$("#closeBoxDiv").hide();
+	$("#closeBox").attr('checked',false);
+	$("#emailBoxDiv").hide();
+	$("#emailBox").attr('checked',false);
+	// Now we'll set values for upload requests with associated event handlers
+	for (var i=0;i<this.uploaderConfig.length;i++) {
+		if (this.uploaderConfig[i].eventTriggerId==selectVal) { // && this.uploaderConfig[i].eventScope==eventHandlerScope) {
+			var uploaderConfig = this.uploaderConfig[i];
+
+			if (typeof uploaderConfig.showExtractOption !== 'undefined') {
+				if (uploaderConfig.showExtractOption) {
+					$("#extractRequestBoxDiv").show();
+				} else {
+					$("#extractRequestBoxDiv").hide();
+				}
+			} else {
+				$("#extractRequestBox").show();
+			}
+			if (typeof uploaderConfig.extractOptionChecked !== 'undefined') {
+				if (uploaderConfig.extractOptionChecked) {
+					$("#extractRequestBox").attr('checked',true);
+				} else {
+					$("#extractRequestBox").attr('checked',false);
+				}
+			} else {
+				$("#extractRequestBox").attr('checked',true);
+			}
+
+			if (typeof uploaderConfig.showCloseWindowOption !== 'undefined') {
+				if (uploaderConfig.showCloseWindowOption) {
+					$("#closeBoxDiv").show();
+				} else {
+					$("#closeBoxDiv").hide();
+				}
+			} else {
+				$("#closeBoxDiv").show();
+			}
+			if (typeof uploaderConfig.closeWindowOptionChecked !== 'undefined') {
+				if (uploaderConfig.closeWindowOptionChecked) {
+					$("#closeBox").attr('checked',true);
+				} else {
+					$("#closeBox").attr('checked',false);
+				}
+			} else {
+				$("#closeBox").attr('checked',false);
+			}
+
+			if (typeof uploaderConfig.showEmailOption !== 'undefined') {
+				if (uploaderConfig.showEmailOption) {
+					$("#emailBoxDiv").show();
+				} else {
+					$("#emailBoxDiv").hide();
+				}
+			} else {
+				$("#emailBoxDiv").show();
+			}
+			if (typeof uploaderConfig.emailOptionChecked !== 'undefined') {
+				if (uploaderConfig.emailOptionChecked) {
+					$("#emailBox").attr('checked',true);
+				} else {
+					$("#emailBox").attr('checked',false);
+				}
+			} else {
+				$("#emailBox").attr('checked',false);
+			}
+			//console.log($("#extractRequestBox").is(":checked"));
+			//console.log($("#closeBox").is(":checked"));
+			//console.log($("#emailBox").is(":checked"));
+
+			break;
+		}
+	}
+	abu._fileUploader.updateOptionStatus();
+}
+
 XNAT.app.abu.whatToDoChange = function(){
 	XNAT.app.abu.whatToDo = $('#whatToDoSelect').val();
         var whatToDo = XNAT.app.abu.whatToDo;
@@ -774,15 +891,19 @@ XNAT.app.abu.whatToDoChange = function(){
 	$('#resourceSelect').val(resourceSelect);
 	XNAT.app.abu.updateModalAction();
 	$('#eventHandlerSelect').val(launchSelect);
+	XNAT.app.abu.updateOptionsCheckboxes(launchSelect);
 	if (typeof abu !== 'undefined' && abu._fileUploader.uploadsStarted>0 && abu._fileUploader.uploadsInProgress==0) {
-		$("#abu-process-button").removeClass("abu-button-disabled");
-		$("#abu-process-button-text").html("Process files");
-		$("#abu-process-button").css("visibility","visible");
+		$("#xmodal-abu-process-button")
+			.prop("disabled","disabled")
+			.html("Process files")
+			.show();
 	}
 	if (XNAT.app.abu.usageSelect == 'Upload' && $('#whatToDoSelect option').size()>1 && $('#whatToDoSelect').val()=="") {
+		$(".abu-upload-button").prop("disabled","disabled");
 		$("#abu-upload-button").addClass("abu-button-disabled");
 		abu._fileUploader.DRAG_AND_DROP_ON = false;
 	} else if (typeof abu == 'undefined' || abu._fileUploader.uploadsStarted==0) {
+		$(".abu-upload-button").prop("disabled",false);
 		$("#abu-upload-button").removeClass("abu-button-disabled");
 		abu._fileUploader.DRAG_AND_DROP_ON = true;
 	} 
@@ -943,6 +1064,7 @@ XNAT.app.abu.processFiles=function() {
 				id: 'xmodal-passed-param',  
 				title: "Information required",
 				content: "<div id='modalParamsToPassDiv'></div>",
+				overflow: "hidden",
 				ok: 'show',
 				okLabel: 'Continue',
 				okAction: function(modl){ 
@@ -963,8 +1085,10 @@ XNAT.app.abu.processFiles=function() {
 			xmodal.open(pModalOpts);
 			paramText='';
 			for (var i=0;i<this.paramsToPass.length;i++) {
-				paramText+='<tr><td style="padding-bottom:5px"><span id="passParamText' + i + '" class="passParamText" style="font-weight:bold">' + this.paramsToPass[i].name + 
-						'</span></td><td style="padding-bottom:5px"><input type="text" size="30" id="passParamInput' + i + '" class="passParamInput"></td></tr>';
+				paramText+='<tr><td style="padding-bottom:5px"><span id="passParamText' + i + '" class="passParamText" style="font-weight:bold">' + this.paramsToPass[i].name +
+					'</span></td><td style="padding-bottom:5px"><input type="text" size="30" id="passParamInput' + i + '" class="passParamInput" value="' + this.paramsToPass[i].defaultVal + '"></td>' +
+					'<td style="padding-bottom:5px;"><span id="passParamDesc' + i + '" class="passParamDesc" style="margin-left:10px;width:300px;float:left;">' + this.paramsToPass[i].description + "</span></td>" +
+					'</tr>';
 			}
 			$('#modalParamsToPassDiv').html('<div id="passParamErrorDiv" class="error hidden"></div><h3>' +
 				 ((this.paramsToPass.length>0) ? "Please supply values for the following parameters:" :  "Please supply a value for the following parameter:") +
@@ -1052,7 +1176,7 @@ XNAT.app.abu.continueProcessing=function() {
 			//if (XNAT.app.abu.usageSelect=='Upload') {
 			//	$("#resourceSelect").prop('disabled',false);
 			//} else if (XNAT.app.abu.usageSelect=='Launch') { 
-			//	$("#abu-process-button").removeClass("abu-button-disabled");
+			//	$("#abu-process-button").prop("disabled",false);
 			//}
 		});
 		processAjax.fail( function( data, textStatus, jqXHR ) {
@@ -1071,10 +1195,10 @@ XNAT.app.abu.continueProcessing=function() {
 			//if (XNAT.app.abu.usageSelect=='Upload') {
 			//	$("#resourceSelect").prop('disabled',false);
 			//} else if (XNAT.app.abu.usageSelect=='Launch') { 
-			//	$("#abu-process-button").removeClass("abu-button-disabled");
+			//	$("#abu-process-button").prop("disabled",false);
 			//}
 		});
-		$("#abu-done-button").removeClass("abu-button-disabled");
+		$("#xmodal-abu-done-button").show();
 		setTimeout(function(){
 			if (document.getElementById("closeBox")!=null && document.getElementById("closeBox").checked) {
 				xmodal.message('Notice',"You will be sent an e-mail upon completion");
@@ -1110,15 +1234,27 @@ XNAT.app.abu.saveUploaderConfiguration=function(configTriggerId, configEvent, sc
 	newConfigObj.launchWithoutUploads = $('#ULC_RB_launchWithoutUploads').is(':checked');
 	newConfigObj.doNotUseUploader = $('#ULC_RB_doNotUseUploader').is(':checked');
 	newConfigObj.escapeHtml = $('#ULC_RB_outputText').is(':checked');
+
+	newConfigObj.showExtractOption = $('#ULC_RB_showExtractOption').is(':checked');
+	newConfigObj.extractOptionChecked = $('#ULC_RB_extractOptionChecked').is(':checked');
+	newConfigObj.showCloseWindowOption = $('#ULC_RB_showCloseWindowOption').is(':checked');
+	newConfigObj.closeWindowOptionChecked = $('#ULC_RB_closeWindowOptionChecked').is(':checked');
+	newConfigObj.showEmailOption = $('#ULC_RB_showEmailOption').is(':checked');
+	newConfigObj.emailOptionChecked = $('#ULC_RB_emailOptionChecked').is(':checked');
+
 	newConfigObj.parameters = undefined;
 	$(".ULC_parametersDiv").each(function() {
 		var parameterField = $(this).find(".ULC_parametersField").val();
+		var parameterDefault = $(this).find(".ULC_parametersDefault").val();
+		var parameterDesc = $(this).find(".ULC_parametersDesc").val();
 		if (typeof(parameterField)!=='undefined' && parameterField != null && parameterField.replace('/ /g','').length>0) {
 			if (typeof(newConfigObj.parameters)==='undefined' || newConfigObj.parameters == null) {
 				newConfigObj.parameters = [];
 			}
 			var newParam = {};
 			newParam.name = parameterField.trim();
+			newParam.defaultVal = parameterDefault.trim();
+			newParam.description = parameterDesc.trim();
 			newParam.type = $(this).find(".ULC_parametersType").val();
 			newParam.required = $(this).find(".ULC_parametersRequired").is(':checked');
 			newConfigObj.parameters.push(newParam);
@@ -1253,6 +1389,14 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 		configObj.launchWithoutUploads = false;
 		configObj.doNotUseUploader = true;
 		configObj.escapeHtml = false;
+
+		configObj.showExtractOption = true;
+		configObj.extractOptionChecked = true;
+		configObj.showCloseWindowOption = true;
+		configObj.closeWindowOptionChecked = false;
+		configObj.showEmailOption = true;
+		configObj.emailOptionChecked = false;
+
 		// best to leave these undefined, I think
 		//configObj.parameters = [  ];
 		//configObj.contexts = [ 'xnat:projectData','xnat:subjectAssessorData','xnat:imageAssessorData','xnat:imageSessionData','xnat:imageScanData','xnat:subjectData' ];
@@ -1270,6 +1414,8 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 				close: false,
 				action: function( obj ){
 					if (XNAT.app.abu.validateUploaderConfiguration()) {
+						$('#ULC_RB_emailOptionChecked').prop("disabled",false);
+						$('#ULC_RB_showCloseWindowOption').prop("disabled",false);
 						XNAT.app.abu.saveUploaderConfiguration(configTriggerId, configEvent, scope);
 						obj.close();
 					}
@@ -1308,7 +1454,27 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 	configHtml+='<div style="margin-left:20px;width:100%"><input type="radio" id="ULC_RB_outputText" name="ULC_RB_OUTPUT" value="outputText"' +
 				 ((configObj.escapeHtml) ? ' checked' : '') + '> <b> Text (escape HTML characters) </b> </div>';
 	configHtml+='</div></p>';
+
+	configHtml+='<p>';
+	configHtml+='<div style="margin-left:20px;width:100%"><p><b>Upload window options:</b>';
+	configHtml+='<div style="margin-left:20px;width:100%"><input type="checkbox" id="ULC_RB_showExtractOption" name="ULC_RB_SHOWEXTRACT" value="showExtractOption"' +
+				 ((configObj.showExtractOption) ? ' checked' : '') + '> <b> Show <i>extract compressed files</i> option?</b> </div>';
+	configHtml+='<div style="margin-left:20px;width:100%"><input type="checkbox" id="ULC_RB_extractOptionChecked" name="ULC_RB_EXTRACTCHECKED" value="extractOptionChecked"' +
+				 ((configObj.extractOptionChecked) ? ' checked' : '') + '> <b> <i>Extract compressed files</i> option checked?</b> </div>';
+
+
+	configHtml+='<div style="margin-left:20px;width:100%;margin-top:10px;"><input type="checkbox" id="ULC_RB_showCloseWindowOption" name="ULC_RB_SHOWCLOSE" value="showCloseWindowOption"' +
+				 ((configObj.showCloseWindowOption) ? ' checked' : '') + '> <b> Show <i>close window upon submit</i> option?</b> </div>';
+	configHtml+='<div style="margin-left:20px;width:100%"><input type="checkbox" id="ULC_RB_closeWindowOptionChecked" name="ULC_RB_CLOSECHECKED" value="closeWindowOptionChecked"' +
+				 ((configObj.closeWindowOptionChecked) ? ' checked' : '') + '> <b> <i>Close window upon submit</i> option checked?</b> </div>';
+
+
+	configHtml+='<div style="margin-left:20px;width:100%;margin-top:10px;"><input type="checkbox" id="ULC_RB_showEmailOption" name="ULC_RB_SHOWEMAIL" value="showEmailOption"' +
+				 ((configObj.showEmailOption) ? ' checked' : '') + '> <b> Show <i>send e-mail upon completion</i> option?</b> </div>';
+	configHtml+='<div style="margin-left:20px;width:100%"><input type="checkbox" id="ULC_RB_emailOptionChecked" name="ULC_RB_EMAILCHECKED" value="emailOptionChecked"' +
+				 ((configObj.emailOptionChecked) ? ' checked' : '') + '> <b> <i>Send e-mail upon completion</i> option checked?</b> </div>';
 	configHtml+='<p>';
+
 	configHtml+='<div style="margin-left:20px;width:100%"><p><b>User Supplied Parameters:</b><p><div id="ULC_parameters">';
 	for (var i=0;i<((typeof(configObj.parameters)!=='undefined' && configObj.parameters.length>0) ? configObj.parameters.length : 0);i++) {
 		var hasValue = (typeof(configObj.parameters)!=='undefined' && configObj.parameters.length>=(i+1));
@@ -1317,15 +1483,21 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 		var integerSelected = (hasValue && typeof(configObj.parameters[i].type)!==undefined && configObj.parameters[i].type=='Integer') ? 'selected' : '';
 		var floatSelected = (hasValue && typeof(configObj.parameters[i].type)!==undefined && configObj.parameters[i].type=='Float') ? 'selected' : '';
 		var fieldRequired = (hasValue && typeof(configObj.parameters[i].required)!==undefined && configObj.parameters[i].required==false) ? '' : 'checked';
+		var fieldDefault = (hasValue && typeof(configObj.parameters[i].defaultVal)!==undefined) ? configObj.parameters[i].defaultVal : '';
+		var fieldDesc = (hasValue && typeof(configObj.parameters[i].description)!==undefined) ? configObj.parameters[i].description : '';
 		configHtml+='<div id="ULC_parametersDiv' + i + '" class="ULC_parametersDiv" style="margin-left:20px;margin-bottom:5px;width:100%">' +
-				 '<input type="text" size="20"  id="ULC_parametersField' + i + '"  class="ULC_parametersField" value="' + fieldValue + '">' + 
-				'  <select id="ULC_parametersType' + i  + '" class="ULC_parametersType">' + 
-				'<option value="String" ' + stringSelected + '>String</option>' + 
+			'<div style="float:left"><div class="abu-config-param-grp"><div class="abu-config-param-row">' +
+			'<div class="abu-config-param-div">Name:</div><input type="text" size="20"  id="ULC_parametersField' + i + '" class="ULC_parametersField" value="' + fieldValue + '" > ' +
+			'<select style="margin-left:5px" id="ULC_parametersType' + i  + '" class="ULC_parametersType">' +
+			'<option value="String" ' + stringSelected + '>String</option>' +
 				'<option value="Integer" ' + integerSelected + '>Integer</option>' + 
 				'<option value="Float" ' + floatSelected + '>Float</option>' + 
-				'</select>  ' + 
-				' <input type="checkbox" id="ULC_parametersRequired"' + i + '" ' + fieldRequired + 
-				' class="ULC_parametersRequired"> Required? <button id="ULC_parametersRemove' + i + '" class="parametersRemove">Remove</button></div>';
+				'</select>  ' +
+			'<input type="checkbox" style="margin-left:5px" id="ULC_parametersRequired"' + i + '" ' + fieldRequired + ' class="ULC_parametersRequired"> Required? </div>' +
+			'<div class="abu-config-param-row"><div class="abu-config-param-div">Default Value:</div><input type="text" size="20"  id="ULC_parametersDefault' + i + '"  class="ULC_parametersDefault" value="' + fieldDefault + '"></div>' +
+			'<div class="abu-config-param-row"><div class="abu-config-param-div">Description Text:</div><input type="text" size="50"  id="ULC_parametersDesc' + i + '"  class="ULC_parametersDesc" value="' + fieldDesc + '"></div></div>' +
+			'<button style="margin-left:10px" id="ULC_parametersRemove' + i + '" class="parametersRemove">Remove</button></div>' +
+			'</div>';
 	} 
 	configHtml+='</div>';
 	configHtml+='<span style="margin-left:20px"><button class="parametersAdd">Add Parameter</button></span>';
@@ -1379,6 +1551,7 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 	configHtml+='</div><span style="margin-left:20px"><button class="ULC_contextsAdd">Add Context</button></span></div></p>';
 
 	$('#configUploadDiv').html(configHtml); 
+	XNAT.app.abu.updateEmailOptionStatus();
 	$(".ULC_contextsDiv :input,.ULC_contextsAdd").prop('disabled',$('#ULC_contextsAllCB').is(':checked') || !( $('#ULC_RB_launchFromResourceUploads').is(':checked') || 
 		$('#ULC_RB_launchFromCacheUploads').is(':checked') || $('#ULC_RB_launchWithoutUploads').is(':checked')) );
 
@@ -1420,11 +1593,19 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 		$(".ULC_contextsDiv :input,.ULC_contextsAdd").prop('disabled',$('#ULC_contextsAllCB').is(':checked') || !( $('#ULC_RB_launchFromResourceUploads').is(':checked') || 
 			$('#ULC_RB_launchFromCacheUploads').is(':checked') || $('#ULC_RB_launchWithoutUploads').is(':checked')) );
 	 });
+	$('#ULC_RB_closeWindowOptionChecked').change(function() {
+		XNAT.app.abu.updateEmailOptionStatus()
+		});
+	$('#ULC_RB_showEmailOption').change(function() {
+		XNAT.app.abu.updateEmailOptionStatus()
+		});
 
 	var ULC_addParameter = function(){
 
 		var divs = $(".ULC_parametersDiv");
 		var fields = $(".ULC_parametersField");
+		var defaultVal = $(".ULC_parametersDefault");
+		var desc = $(".ULC_parametersDesc");
 		for (var i=0;i<fields.length;i++) {
 			var val = $(fields.get(i)).val();
 			if (typeof(val)==='undefined' || val == null || val.length<1) {
@@ -1435,14 +1616,18 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 		var len = (divs.length>0) ? Number(($(divs).last().get(0)).id.replace("ULC_parametersDiv",""))+1 : 1;
 		$("#ULC_parameters").append(
 				'<div id="ULC_parametersDiv' + len + '" class="ULC_parametersDiv" style="margin-left:20px;margin-bottom:5px;width:100%">' +
-				'<input type="text" size="20"  id="ULC_parametersField' + i + '" class="ULC_parametersField"> ' + 
-				' <select id="ULC_parametersType' + len  + '" class="ULC_parametersType">' + 
+				'<div style="float:left"><div class="abu-config-param-grp"><div class="abu-config-param-row"><div class="abu-config-param-div">Name:</div><input type="text" size="20"  id="ULC_parametersField' + i + '" class="ULC_parametersField"> ' +
+				'<select style="margin-left:5px" id="ULC_parametersType' + len  + '" class="ULC_parametersType">' +
 				'<option value="String">String</option>' + 
 				'<option value="Integer">Integer</option>' + 
 				'<option value="Float">Float</option>' + 
-				'</select>  ' + 
-				'<input type="checkbox" id="ULC_parametersRequired"' + len + '" class="ULC_parametersRequired" checked> Required? <button id="ULC_parametersRemove' + len +
-				 '" class="parametersRemove">Remove</button></div>'
+				'</select>' +
+				'<input type="checkbox" style="margin-left:5px" id="ULC_parametersRequired"' + len + '" class="ULC_parametersRequired" checked> Required? </div>' +
+				'<div class="abu-config-param-row"><div class="abu-config-param-div">Default Value:</div><input type="text" size="20"  id="ULC_parametersDefault' + i + '"  class="ULC_parametersDefault"></div>' +
+				'<div class="abu-config-param-row"><div class="abu-config-param-div">Description Text:</div><input type="text" size="50"  id="ULC_parametersDesc' + i + '"  class="ULC_parametersDesc"></div></div>' +
+				'<button style="margin-left:10px" id="ULC_parametersRemove' + len + '" class="parametersRemove">Remove</button></div>' +
+				'</div>'
+
 		);
 		$('#ULC_parametersRemove' + len).click(function(){ 
 			$("#ULC_parametersDiv" + len).remove(); 
@@ -1476,5 +1661,21 @@ XNAT.app.abu.configureUploaderForEventHandler=function(configTriggerId, configEv
 	}
 }
 
+XNAT.app.abu.updateEmailOptionStatus=function() {
+	if ($('#ULC_RB_closeWindowOptionChecked').is(":checked")) {
+		$('#ULC_RB_emailOptionChecked').prop("checked",true);
+		$('#ULC_RB_emailOptionChecked').prop("disabled",true);
+		if ($('#ULC_RB_showEmailOption').is(":checked")) {
+			$('#ULC_RB_showCloseWindowOption').prop("checked",true);
+			$('#ULC_RB_showCloseWindowOption').prop("disabled",true);
+		} else {
+			$('#ULC_RB_showCloseWindowOption').prop("disabled",false);
+		}
+	} else {
+		$('#ULC_RB_emailOptionChecked').prop("disabled",false);
+		$('#ULC_RB_showCloseWindowOption').prop("disabled",false);
+	}
+}
+
 $(document).ready(XNAT.app.abu.abuConfigs.load);
 
diff --git a/src/main/webapp/scripts/uploaders/fileuploader.js b/src/main/webapp/scripts/uploaders/fileuploader.js
index f355e93c1431e625312d981cfb9c66a745cbf3f7..e13ba430982dead6f4dcd27baea10428e9e6567b 100644
--- a/src/main/webapp/scripts/uploaders/fileuploader.js
+++ b/src/main/webapp/scripts/uploaders/fileuploader.js
@@ -38,74 +38,61 @@ abu.FileUploader = function(o){
 		$(this._options.element).append(
 			'<div class="abu-uploader">' +
 				'<div id="abu-files-processing" class="abu-files-processing">        Processing...... </div>' +
-				'<a id="file-uploader-instructions-sel" class="abu-uploader-instructions-sel" onclick="abu._fileUploader.uploaderHelp()">?</a>' +
+				'<a id="file-uploader-instructions-sel" class="abu-uploader-instructions-sel" onclick="abu._fileUploader.uploaderHelp()"><span class="icon icon-sm icon-qm"></span>Help</a>' +
 				'<div class="abu-upload-drop-area" style="display: none;"><span>Drop files here to upload</span></div>' +
 				'<div class="abu-xnat-interactivity-area">' +
+					'<div class="abu-xnat-interactivity-area-contents"></div>' +
+					'<div class="abu-options-div">' +
+					((this._options.showExtractOption) ?
+						'<div class="abu-options-cb" id="extractRequestBoxDiv" title = "Extract compressed files on upload (zip, tar, gz)"?>' +
+						'<input id="extractRequestBox" type="checkbox" value="1" checked="checked">' +
+						'Extract compressed files' +
+						'</div>' :
+							'<div class="abu-extract-zip"><input id="extractRequestBox" type="hidden" value="1"/></div>'
+					) +
+					((this._options.showCloseOption) ?
+						'<div class="abu-options-cb" id="closeBoxDiv" title = "Close window upon submit and send e-mail upon completion">' +
+						'<input id="closeBox" type="checkbox" value="1">' +
+						'Close window upon submit' +
+						'</div>' : ""
+					) +
+					((this._options.showEmailOption) ?
+						'<div class="abu-options-cb" id="emailBoxDiv" title = "Send e-mail upon completion">' +
+						'<input id="emailBox" type="checkbox" value="1">' +
+						'Send e-mail upon completion' +
+						'</div>' : ""
+					) +
+					((this._options.showUpdateOption) ?
+						'<div class="abu-options-cb" id="updateBoxDiv" title = "Update existing records?">' +
+						'<input id="updateBox" type="checkbox" value="1">' +
+						'Update existing records?' +
+						'</div>' : ""
+					) +
+					((this._options.showVerboseOption) ?
+						'<div class="abu-options-cb" id="verboseBoxDiv" title = "Verbose status output?">' +
+						'<input id="verboseBox" type="checkbox" value="1"' +
+						'Verbose status output?' +
+						'</div>' : ""
+					) +
+					'</div>' +
 				'</div>' +
 			'<div id="abu-upload-button" class="abu-upload-button" style="position: relative; overflow: hidden; direction: ltr;">' + 
 				'Upload files<input multiple="multiple" type="file" id="file-upload-input" class="abu-button-input">' + 
 			'</div>' +
-			'<div id="abu-done-button" class="abu-done-button" style="position: relative; overflow: hidden; direction: ltr;">' + 
-				'<span id="abu-done-button-text" class="abu-done-button-cancel">Cancel</span><input type="image" name="done" class="abu-button-input" style="width:105px">' +
-			'</div>' +
-			'<div id="abu-process-button" class="abu-process-button " style="position: relative; overflow: hidden; direction: ltr;">' +
-				'<span id="abu-process-button-text">Process Files</span>' +
-				'<input type="image" name="process" class="abu-button-input" style="width:105px">' +
-			'</div>' +
-			'<div class="abu-options-div">' +
-			((this._options.showExtractOption) ?
-				'<div class="abu-options-cb" title = "Extract compressed files on upload (zip, tar, gz)"?>' +
-					'<input id="extractRequestBox" type="checkbox" value="1" checked="checked">' +
-					'Extract compressed files' +
-				'</div>' : 
-				'<div class="abu-extract-zip"><input id="extractRequestBox" type="hidden" value="1"/></div>'
-			) +
-			((this._options.showCloseOption) ?
-				'<div class="abu-options-cb" title = "Close window upon submit and send e-mail upon completion">' +
-					'<input id="closeBox" type="checkbox" value="1">' +
-					'Close window upon submit' +
-				'</div>' : "" 
-			) +
-			((this._options.showEmailOption) ?
-				'<div class="abu-options-cb" title = "Send e-mail upon completion">' +
-					'<input id="emailBox" type="checkbox" value="1">' +
-					'Send e-mail upon completion' +
-				'</div>' : "" 
-			) +
-			((this._options.showUpdateOption) ?
-				'<div class="abu-options-cb" title = "Update existing records?">' +
-					'<input id="updateBox" type="checkbox" value="1">' +
-					'Update existing records?' +
-				'</div>' : "" 
-			) +
-			((this._options.showVerboseOption) ?
-				'<div class="abu-options-cb" title = "Verbose status output?">' +
-					'<input id="verboseBox" type="checkbox" value="1"' +
-					'Verbose status output?' +
-				'</div>' : "" 
-			) +
-			'</div><br>' +
 			'<div class="abu-list-area"><ul class="abu-upload-list"></ul>' +
 				'<div class="response_text" style="display:none"></div>' +
 			'</div> ' 
 		); 
-		$("#abu-upload-button").mouseenter(function() { $(this).addClass("abu-upload-button-hover"); });
-		$("#abu-upload-button").mouseleave(function() { $(this).removeClass("abu-upload-button-hover"); });
-		$("#abu-upload-button").click(function() { $("#abu-done-button").removeClass("abu-button-disabled"); });
-		$("#abu-done-button").click(this._options.doneFunction);
-		$("#abu-done-button").mouseenter(function() { $(this).addClass("abu-done-button-hover"); });
-		$("#abu-done-button").mouseleave(function() { $(this).removeClass("abu-done-button-hover"); });
-		$("#abu-process-button").click(this._options.processFunction);
-		$("#abu-process-button").mouseenter(function() { $(this).addClass("abu-process-button-hover"); });
-		$("#abu-process-button").mouseleave(function() { $(this).removeClass("abu-process-button-hover"); });
+		// $("#abu-upload-button").click(function() { $("#abu-done-button").removeClass("abu-button-disabled"); });
+		$("#xmodal-abu-done-button").click(this._options.doneFunction);
+
+		// replaced #abu-process-button with #xmodal-abu-process-button, which is defined in the xmodal options
+		$("#xmodal-abu-process-button").click(this._options.processFunction);
 		$('#closeBox').change(function(){ 
-			if ($('#closeBox').is(':checked')) { 
-				$('#emailBox').prop('checked', true);
-				$('#emailBox').attr('disabled', true);
-			} else {
-				$('#emailBox').attr('disabled', false);
-			}
-		 });
+			this.updateOptionStatus();
+		 }.bind(this));
+
+		this.updateOptionStatus();
 
 		if (this.ALLOW_DRAG_AND_DROP) {
 			$(".abu-upload-drop-area").on('dragleave',function(e) { 
@@ -193,7 +180,9 @@ abu.FileUploader = function(o){
 			if (typeof eventData.target.files !== 'undefined') {
 				var fileA = eventData.target.files;
 				if (fileA.length==0) {
-					$("#abu-done-button").removeClass("abu-button-disabled");
+					$("#xmodal-abu-done-button")
+						.show()
+						.prop("disabled",false);
 				} 
 				this.doFileUpload(fileA);
 			}
@@ -201,15 +190,23 @@ abu.FileUploader = function(o){
 	}
 
 	this.processingComplete = function() {
-		$("#abu-done-button-text").html("Done");
-		$("#abu-done-button-text").addClass("abu-done-button-done");
-		$("#abu-done-button-text").removeClass("abu-done-button-cancel");
-		//$("#abu-upload-button").css("display","None");
-		$("#abu-process-button").addClass("abu-button-disabled");
-		//$("#abu-process-button-text").html("&nbsp;");
-		$("#abu-process-button").css("visibility","hidden");
-		$("#abu-upload-button").addClass("abu-button-disabled");
-		$("#abu-files-processing").css("display","None");
+		// $("#xmodal-abu-done-button").html("Done");
+		// $("#abu-done-button-text").addClass("abu-done-button-done");
+		// $("#abu-done-button-text").removeClass("abu-done-button-cancel");
+		// $("#abu-upload-button").css("display","None");
+		$("#xmodal-abu-cancel-button").hide();
+		$("#xmodal-abu-done-button")
+			.prop("disabled",false)
+			.show();
+		$("#xmodal-abu-process-button")
+			.hide()
+			.prop("disabled","disabled");
+		// $("#abu-process-button-text").html("&nbsp;");
+		// $("#abu-process-button").css("visibility","hidden");
+		$(".abu-upload-button")
+			.hide()
+			.prop("disabled","disabled");
+		$("#abu-files-processing").hide();
 		$(".abu-uploader").css("overflow-y","auto");
 		$(".abu-uploader").css("overflow-x","hidden");
 	}
@@ -243,6 +240,15 @@ abu.FileUploader = function(o){
 		}
 	}.bind(this)
 
+	this.updateOptionStatus = function() {
+		if ($('#closeBox').is(':checked')) { 
+			$('#emailBox').prop('checked', true);
+			$('#emailBox').attr('disabled', true);
+		} else {
+			$('#emailBox').attr('disabled', false);
+		}
+	}
+
 	this.bytesToSize = function(bytes) {
 	   var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
 	   if (bytes == 0) return '0 Byte';
@@ -338,7 +344,9 @@ abu.FileUploader = function(o){
 			 		status.html('<a href="javascript:abu._fileUploader.showReturnedText(\'' + $(status).attr('id') + '\')" class="underline abu-upload-fail">Failed</a>');
 			 		status.css("display","inline-block");
 			 		$(infoSelector).find(".abu-progress").css("display","none");
-					$("#abu-done-button").removeClass("abu-done-button-disabled");
+					$("#xmodal-abu-done-button")
+						.show()
+						.prop("disabled",false);
 				},
 				success: function(result) {
 					$(status).data("rtn",result);
@@ -364,7 +372,7 @@ abu.FileUploader = function(o){
 				 			status.html('<span class="abu-upload-complete abu-upload-complete-text">Upload complete' + 
 								((this.isOverwrite) ? ' (Existing file overwritten) ' : '') + '</span>');
 						}
-						$("#abu-done-button-text").addClass("abu-done-button-file-uploaded");
+						$("#xmodal-abu-done-button-text").addClass("abu-done-button-file-uploaded");
 					} else {
 			 			status.html('<a href="javascript:abu._fileUploader.showReturnedText(\'' + $(status).attr('id') + '\')" class="underline abu-upload-fail">Duplicate file and overwrite=false.  Not uploaded.</a>');
 					}
diff --git a/src/main/webapp/scripts/utils.js b/src/main/webapp/scripts/utils.js
index a5d5590a22d6e2b69c29eb326a79ac693a07ae8f..bc0b3b75479ab9c891ec35c3f9af01d2fba7453c 100755
--- a/src/main/webapp/scripts/utils.js
+++ b/src/main/webapp/scripts/utils.js
@@ -816,11 +816,11 @@ $.fn.superSimpleAccordion = function(){
     // add styling to <head> element
     $('head').append('<style type="text/css" id="accordion-styles">' +
         '#accordion { font-family: Arial, Helvetica, sans-serif; font-size: 13px; line-height: 17px; }' +
-        '#accordion .active { display: block; }' +
-        '#accordion h3 { font-size: 13px; font-weight: bold; color: #222; padding: 5px 10px; border: 1px solid #d0d0d0; }' +
-        '#accordion h3:hover { cursor: pointer; }' +
-        '#accordion h3.active { background: #1A75BB; color: #fff; } ' +
-        '#accordion .content { padding: 1em; border: 1px solid #d0d0d0; }' +
+        '#accordion > .active { display: block; }' +
+        '#accordion > h3 { font-size: 13px; font-weight: bold; color: #222; padding: 5px 10px; border: 1px solid #d0d0d0; }' +
+        '#accordion > h3:hover { cursor: pointer; }' +
+        '#accordion > h3.active { background: #1A75BB; color: #fff; } ' +
+        '#accordion > .content { padding: 1em; border: 1px solid #d0d0d0; }' +
         '</style>');
 
 };
diff --git a/src/main/webapp/scripts/xmodal-v1/xmodal.css b/src/main/webapp/scripts/xmodal-v1/xmodal.css
index 3c67d3c141f99bc9d81515d14ad59daed25f9955..bfd32b7902aaa8e68316eb1666dddb6507b279bb 100644
--- a/src/main/webapp/scripts/xmodal-v1/xmodal.css
+++ b/src/main/webapp/scripts/xmodal-v1/xmodal.css
@@ -21,7 +21,7 @@ div.xmodal-mask { background: transparent url(data:image/png;base64,iVBORw0KGgoA
 /*div.xmodal-mask.open { display: block ; z-index: 100 ; }*/
 
 div.xmodal {
-    display: none ; min-width: 200px ; min-height: 128px ; max-width: 98% ; max-height: 98% ;
+    display: none ; min-width: 200px ; min-height: 128px ; max-width: 92% ; max-height: 92% ;
     position: fixed ; top: 0 ; right: 0 ; bottom: 0 ; left: 0 ; margin: auto ; overflow: hidden ;
     background: #fff ; border: 1px solid #a0a0a0 ; box-shadow: 0 10px 30px rgba(0,0,0,0.5) ;
     font-family: Arial, Helvetica, sans-serif ; }
@@ -72,10 +72,11 @@ div.xmodal .body > .inner > iframe {
 div.xmodal .body .inner { display: none ; padding: 20px ; font-size: 13px ; line-height: 17px ; }
 div.xmodal.nopad .body .inner { padding: 0 ; }
 div.xmodal.open .body .inner { display: block ; }
+/* Only styles for the dialog elements should be in this file */
 div.xmodal .body h1,
 div.xmodal .body h2,
 div.xmodal .body h3,
-div.xmodal .body h4 { margin-bottom: 1em !important; }
+div.xmodal .body h4 { /* margin-bottom: 1em !important; */ }
 div.xmodal .body p { margin: 0 0 1em ; }
 
 div.xmodal .footer {
diff --git a/src/main/webapp/scripts/xmodal-v1/xmodal.js b/src/main/webapp/scripts/xmodal-v1/xmodal.js
index 55531beacc4b02fac105048644d4f2a7ae1b1caa..80c6bb6f46e44bbc49d36ca97802b902017f0a6d 100644
--- a/src/main/webapp/scripts/xmodal-v1/xmodal.js
+++ b/src/main/webapp/scripts/xmodal-v1/xmodal.js
@@ -450,7 +450,7 @@ if (typeof jQuery == 'undefined') {
 
             resizeBody($modal, modal);
 
-            $(window).resize(function () {
+            $(window).off('resize').on('resize', function(){
                 resizeBody($modal, modal);
             });
 
@@ -711,12 +711,12 @@ if (typeof jQuery == 'undefined') {
             this.top = firstDefined(_opts.top||undefined, '22px');
 
             this.width = 600;
-            this.minWidth = 'inherit';
-            this.maxWidth = 'inherit';
+            //this.minWidth = 'inherit';
+            //this.maxWidth = 'inherit';
 
             this.height = 400;
-            this.minHeight = 'inherit';
-            this.maxHeight = 'inherit';
+            //this.minHeight = 'inherit';
+            //this.maxHeight = 'inherit';
 
             // support for 'preset' sizes
             if (_opts.kind || _opts.size){
@@ -724,8 +724,8 @@ if (typeof jQuery == 'undefined') {
                     var kinds = {
                         dialog:  [600, 400],
                         message: [400, 200],
-                        max:     ['98%', '96%'],
-                        full:    ['98%', '96%'],
+                        max:     ['98%', '98%'],
+                        full:    ['98%', '98%'],
                         large:   ['80%', '80%'],
                         med:     ['60%', '60%'],
                         small:   ['40%', '40%'],
diff --git a/src/main/webapp/scripts/xnat/admin/pluginSettings.js b/src/main/webapp/scripts/xnat/admin/pluginSettings.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe547b20193fde0e3329447b7535eea8abfc58be
--- /dev/null
+++ b/src/main/webapp/scripts/xnat/admin/pluginSettings.js
@@ -0,0 +1,89 @@
+/*!
+ * Plugin site settings functions
+ */
+
+var XNAT = getObject(XNAT);
+
+(function(factory){
+    if (typeof define === 'function' && define.amd) {
+        define(factory);
+    }
+    else if (typeof exports === 'object') {
+        module.exports = factory();
+    }
+    else {
+        return factory();
+    }
+}(function(){
+
+    var pluginSettings;
+
+    XNAT.admin =
+        getObject(XNAT.admin);
+
+    XNAT.admin.pluginSettings = pluginSettings =
+        getObject(XNAT.admin.pluginSettings);
+
+    pluginSettings.check = function(){
+
+        return XNAT.xhr.get(XNAT.url.restUrl('/xapi/plugins'), function(plugins){
+
+            console.log('/xapi/plugins response:');
+            console.log(plugins);
+
+            var showMenuItem = false;
+
+            if (!isEmpty(plugins)) {
+
+                // calling forOwn() returns the key names
+                var pluginNames = forOwn(plugins);
+
+                console.log('plugin names:');
+                console.log(pluginNames);
+
+                var setPaths = function(names){
+                    var paths = [];
+                    [].concat(names).forEach(function(name){
+                        paths.push(name + '/siteSettings');
+                        // paths.push(name + '/admin');
+                    });
+                    return paths;
+                };
+
+                var pluginSettingsPaths = setPaths(pluginNames);
+
+                function getPluginSpawnerElements(path){
+                    var _url = XNAT.url.restUrl('/xapi/spawner/resolve/' + path);
+                    return XNAT.xhr.get(_url);
+                }
+
+                function lookForSettings(i) {
+                    if (showMenuItem || i === pluginSettingsPaths.length){
+                        // console.log("couldn't do it");
+                        return false;
+                    }
+                    // recursively try to get settings at different places
+                    getPluginSpawnerElements(pluginSettingsPaths[i])
+                        .done(function(){
+                            showMenuItem = true;
+                            $('#view-plugin-settings').removeClass('hidden');
+                        })
+                        .fail(function(){
+                            lookForSettings(++i)
+                        });
+                }
+
+                // do the stuff
+                lookForSettings(0);
+
+            }
+
+        });
+    };
+
+    // call it.
+    pluginSettings.check();
+
+    XNAT.admin.pluginSettings = pluginSettings;
+
+}));
diff --git a/src/main/webapp/scripts/xnat/app/scriptEditor.js b/src/main/webapp/scripts/xnat/app/scriptEditor.js
index 47bfbce421f990749894b679403543a38bd71a87..8a66f6b3ff503f6fc4eec6437ef24cc4df027881 100644
--- a/src/main/webapp/scripts/xnat/app/scriptEditor.js
+++ b/src/main/webapp/scripts/xnat/app/scriptEditor.js
@@ -300,8 +300,9 @@ var XNAT = getObject(XNAT || {});
 
         var opts = {};
         opts.width = 880;
-        opts.height = 720;
-        //opts.top = 100;
+        opts.height = 600;
+        // opts.maxHeight = '98%';
+        // opts.top = 100;
         opts.scroll = false;
         opts.template = $('#script-editor-template');
         opts.title = 'XNAT Script Editor';
diff --git a/src/main/webapp/scripts/xnat/spawner.js b/src/main/webapp/scripts/xnat/spawner.js
index 5bb3765420f4391d2e3559f7f1fe6f342de00f73..e8c661fe7f111dcd57b7c830b3f524913ae5f145 100644
--- a/src/main/webapp/scripts/xnat/spawner.js
+++ b/src/main/webapp/scripts/xnat/spawner.js
@@ -344,8 +344,6 @@ var XNAT = getObject(XNAT);
         // you can pass a config object as the only argument
         opts = cloneObject(firstDefined(opts, getObject(nsPath)));
 
-        console.log(opts);
-
         var url = opts.url || XNAT.url.restUrl('/xapi/spawner/resolve/' + nsPath);
 
         var request = XNAT.xhr.getJSON(extend(true, {
diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js
index b72e956e36b2a65e0e249d78e5df400ddb552737..07fa28f9a92bf7ae1db4772e44d48c39f7678255 100644
--- a/src/main/webapp/scripts/xnat/ui/panel.js
+++ b/src/main/webapp/scripts/xnat/ui/panel.js
@@ -560,7 +560,12 @@ var XNAT = getObject(XNAT || {});
                 // ajaxConfig.data = JSON.stringify(form2js(this, /[:\[\]]/));
                 ajaxConfig.data = JSON.stringify(form2js(inputs, ':', false));
                 ajaxConfig.processData = false;
-                ajaxConfig.contentType = 'application/json';
+                if (opts.contentType === 'text/json') {
+                    ajaxConfig.contentType = opts.contentType;
+                }
+                else {
+                    ajaxConfig.contentType = 'application/json';
+                }
                 //$.ajax(ajaxConfig);
                 // XNAT.xhr.form($form, ajaxConfig);
             }
diff --git a/src/main/webapp/scripts/xnat/ui/select.js b/src/main/webapp/scripts/xnat/ui/select.js
index ef8ef9d84d4d47e9f7aba84acad6ce0adc72bd81..90f97baf5f1d24abc6c93a8f8e5b359cfe71ce1d 100644
--- a/src/main/webapp/scripts/xnat/ui/select.js
+++ b/src/main/webapp/scripts/xnat/ui/select.js
@@ -34,12 +34,24 @@ var XNAT = getObject(XNAT);
     // as methods and properties to the function
     select = getObject(XNAT.ui.select || {});
 
-    function addOption(el, opt){
-        el.appendChild(spawn('option', extend(true, {
-            value: opt.value !== undefined ? opt.value : '',
-            html: opt.html || opt.text || opt.label || opt.value,
-            selected: opt.selected || false
-        }, opt.element )));
+    function addOption(el, opt, sel){
+        var val, txt;
+        if (typeof opt === 'string') {
+            val = opt;
+            txt = opt;
+        }
+        else {
+            val = opt.value !== undefined ? opt.value : '';
+            txt = opt.html || opt.text || opt.label || val;
+        }
+        var option = spawn('option', extend(true, {
+            value: val,
+            html: txt,
+            selected: val === sel || [].concat(sel).indexOf(val) > -1 || opt.selected || false
+        }, opt.element, {
+            //selected: val === sel || [].concat(sel).indexOf(val) > -1 || opt.selected || false
+        }));
+        el.appendChild(option);
     }
     
     // generate JUST the options
@@ -62,7 +74,7 @@ var XNAT = getObject(XNAT);
     select.menu = function(config){
 
         var frag = document.createDocumentFragment(),
-            menu, label;
+            $menu, menu, label;
 
         config = cloneObject(config);
 
@@ -71,22 +83,25 @@ var XNAT = getObject(XNAT);
 
         config.id = config.id || toDashed(config.name) || randomID('menu-', false);
         config.name = config.name || '';
+        config.value = config.value !== undefined ? config.value : '';
 
         config.element = extend(true, {
             id: config.id,
             name: config.name,
-            value: config.value !== undefined ? config.value : '',
+            value: config.value,
             title: config.title || ''
         }, config.element);
 
-        menu = spawn('select', config.element);
+        $menu = $.spawn('select', config.element);
+        menu = $menu[0];
 
-        addOption(menu, { html: 'Select' });
+        // DO NOT add default 'Select' option
+        //addOption(menu, { html: 'Select' });
         
         if (config.options){
             if (Array.isArray(config.options)) {
                 forEach(config.options, function(opt){
-                    addOption(menu, opt);
+                    addOption(menu, opt, config.value);
                 })    
             }
             else {
@@ -99,14 +114,15 @@ var XNAT = getObject(XNAT);
                     else {
                         opt = txt;
                     }
-                    addOption(menu, opt);
+                    addOption(menu, opt, config.value);
                 });
             }
         }
         
         // force menu change event to select 'selected' option
-        $(menu).change();
-        
+        $menu.changeVal(config.value);
+        // $menu.find('[value="' + config.value + '"]').prop('selected', true);
+
         // if there's no label, wrap the 
         // <select> inside a <label> element
         if (!config.label) {
diff --git a/src/main/webapp/scripts/xnat/ui/table.js b/src/main/webapp/scripts/xnat/ui/table.js
index ad832e8915e7e357799424b82883be81562d2c8a..18a4267a2f1b6225d968e3e95674547745ad39f0 100755
--- a/src/main/webapp/scripts/xnat/ui/table.js
+++ b/src/main/webapp/scripts/xnat/ui/table.js
@@ -201,7 +201,7 @@ var XNAT = getObject(XNAT);
     Table.p.rows = function(data, opts){
         var _this = this,
             rows  = [],
-            cols = data[0].length; // first array length determines how many columns
+            cols = (data[0]||[]).length; // first array length determines how many columns
         data = data || [];
         data.forEach(function(row){
             row = row.slice(0, cols);
diff --git a/src/main/webapp/scripts/xnat/ui/tabs.js b/src/main/webapp/scripts/xnat/ui/tabs.js
index 2305742c9dde8fa4c1a51bb0cc51762f14aad204..9542a5ccafa30ad0d35b4a80c8f3cd8dbf24a0e1 100755
--- a/src/main/webapp/scripts/xnat/ui/tabs.js
+++ b/src/main/webapp/scripts/xnat/ui/tabs.js
@@ -50,6 +50,8 @@ var XNAT = getObject(XNAT || {});
     tab.group = function(obj){
         var id = toDashed(obj.id || obj.name);
         if (!id) return; // a tab group MUST have an id
+        // return if the group already exists
+        if ($('ul#' + id + '.nav.tab-group').length) return;
         return spawn('ul.nav.tab-group', { id: id }, [
             ['li.label', (obj.label || obj.title || obj.text || 'Tab Group')]
         ]);
@@ -194,24 +196,23 @@ var XNAT = getObject(XNAT || {});
 
         // use existing tabs if already present
         if ($container.find(NAV_TABS).length) {
-            navTabs = $container.find(NAV_TABS)[0]
+            $navTabs = $container.find(NAV_TABS);
         }
         else {
-            navTabs = spawn(NAV_TABS);
-            $container.append(navTabs);
+            $navTabs = $.spawn(NAV_TABS);
+            $container.append($navTabs);
         }
+        navTabs = $navTabs[0];
 
         // use existing content container if already present
         if ($container.find(TAB_CONTENT).length) {
-            tabContent = $container.find(TAB_CONTENT)[0];
+            $tabContent = $container.find(TAB_CONTENT);
         }
         else {
-            tabContent = spawn(TAB_CONTENT);
-            $container.append(tabContent);
+            $tabContent = $.spawn(TAB_CONTENT);
+            $container.append($tabContent);
         }
-
-        $navTabs = $(navTabs);
-        $tabContent = $(tabContent);
+        tabContent = $tabContent[0];
 
         layout = obj.layout || tabs.layout || 'left';
 
diff --git a/src/main/webapp/scripts/xnat/xhr.js b/src/main/webapp/scripts/xnat/xhr.js
index 734219592e9cfaf93b25789ef93954a3399268bc..b6acb28092ac46116198767b0ee129c3c5b28ec4 100755
--- a/src/main/webapp/scripts/xnat/xhr.js
+++ b/src/main/webapp/scripts/xnat/xhr.js
@@ -578,26 +578,35 @@ var XNAT = getObject(XNAT||{}),
         // stop here if there are errors
         if (errors.length) {
             console.log('ERRORS: ' + errors);
-            // errors = errors.map(function(field){
-            //     return '<li>' + field + '</li>'
-            // });
-            // xmodal.message({
-            //     title: 'Validation Failed',
-            //     content: '' +
-            //         '<p>Please correct errors with the following fields:</p> ' +
-            //         '<ul>' + errors.join('') + '</ul>'
-            // });
-            // return false;
+            // VALIDATION ERRORS WILL STOP FORM SUBMISSION
+            errors = errors.map(function(field){
+                return '<li>' + field + '</li>'
+            });
+            xmodal.message({
+                title: 'Validation Failed',
+                content: '' +
+                    '<p>Please correct errors with the following fields:</p> ' +
+                    '<ul>' + errors.join('') + '</ul>'
+            });
+            return false;
         }
 
         var inputs = $inputs.toArray();
 
         if (/POST|PUT/i.test(opts.method)) {
+            if ($form.hasAnyClass('json-text text-json')) {
+                opts.contentType = 'text/json';
+            }
             if ($form.hasClass('json') || /json/i.test(opts.contentType||'')){
                 // opts.data = formToJSON($form, true);
                 opts.data = JSON.stringify(form2js(inputs, opts.delimiter||opts.delim||':', false));
                 opts.processData = false;
-                opts.contentType = 'application/json';
+                if (opts.contentType === 'text/json') {
+                    opts.contentType = 'text/plain';
+                }
+                else {
+                    opts.contentType = 'application/json';
+                }
             }
             else {
                 opts.data = $form.find(':input').not('.ignore').serialize();
@@ -628,7 +637,7 @@ var XNAT = getObject(XNAT||{}),
     $.fn.submitJSON = function(opts){
         var $form = $(this);
         $form.addClass('json');
-        return xhr.form(this, extend(true, {
+        return xhr.form($form, extend(true, {
             method: $form.data('method') || this.method || 'POST',
             processData: false,
             contentType: 'application/json'
diff --git a/src/main/webapp/scripts/yui/build/assets/skins/sam/sprite.png b/src/main/webapp/scripts/yui/build/assets/skins/sam/sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc06295511c25a8fe7080c5c10d2c6c59e482320
Binary files /dev/null and b/src/main/webapp/scripts/yui/build/assets/skins/sam/sprite.png differ
diff --git a/src/main/webapp/setup/site-setup.yaml b/src/main/webapp/setup/site-setup.yaml
index ea6d480c888b846f6fdaed3e6abfeb2ac673c1f3..516b6db29fdaf85d1cf281401207a9c38ff5d957 100644
--- a/src/main/webapp/setup/site-setup.yaml
+++ b/src/main/webapp/setup/site-setup.yaml
@@ -175,31 +175,31 @@ siteSetup:
 
                 host:
                     kind: panel.input.text
-                    name: host
+                    name: smtpHostname
                     label: Host
                     placeholder: localhost
                     validation: required
 
                 port:
                     kind: panel.input.number
-                    name: port
+                    name: smtpPort
                     label: Port
                     placeholder: 25
                     validation: required number
 
                 username:
                     kind: panel.input.text
-                    name: username
+                    name: smtpUsername
                     label: Username
 
                 password:
                     kind: panel.input.password
-                    name: password
+                    name: smtpPassword
                     label: Password
 
                 protocol:
                     kind: panel.input.text
-                    name: protocol
+                    name: smtpProtocol
                     label: Protocol
 
                 mailServerProperties:
@@ -208,21 +208,21 @@ siteSetup:
 
                 smtpAuth:
                     kind: panel.input.switchbox
-                    name: mail.smtp.auth
+                    name: smtpAuth
                     label: SMTP Auth?
                     onText: Enabled
                     offText: Disabled
 
                 smtpStartTls:
                     kind: panel.input.switchbox
-                    name: mail.smtp.starttls.enable
+                    name: smtpStartTls
                     label: Start TLS?
                     onText: Enabled
                     offText: Disabled
 
-                smtpSSLTrust:
+                smtpSslTrust:
                     kind: panel.input.text
-                    name: mail.smtp.ssl.trust
+                    name: smtpSslTrust
                     label: SSL Trust
 
         # =========================
diff --git a/src/main/webapp/style/app.css b/src/main/webapp/style/app.css
index ab493abdb7652c13ebff5c1be1973d9fa06580b4..98b1e5946abd294313a1a78d811a9d0bd8213b69 100644
--- a/src/main/webapp/style/app.css
+++ b/src/main/webapp/style/app.css
@@ -715,6 +715,26 @@ table.ruled tr:nth-child(2n+1) {
     font-family: Courier, monospace;
 }
 
+.center,
+.centered {
+    text-align: center;
+}
+
+.align-left,
+.text-left {
+    text-align: left;
+}
+
+.align-right,
+.text-right {
+    text-align: right;
+}
+
+.nowrap,
+.no-wrap {
+    white-space: nowrap;
+}
+
 textarea.full {
     width: 100%; height: 100%;
     position: absolute;
diff --git a/src/main/webapp/style/uploaders/fileuploader.css b/src/main/webapp/style/uploaders/fileuploader.css
index 0a6fa0f2098a06431510749306ba84e20201aeaa..1ce9584fc7968bbe04cffb7d8a14d76fcfe1a224 100644
--- a/src/main/webapp/style/uploaders/fileuploader.css
+++ b/src/main/webapp/style/uploaders/fileuploader.css
@@ -9,45 +9,39 @@
 
 .abu-uploader { position:relative; width: 100%; height:435px;}
 
-div.abu-button-disabled {
-    background:#CCCCCC; pointer-events:none 
+.abu-button-disabled {
+    color:#A8A8A8; 
+    pointer-events:none; 
 }
 
-div.abu-xnat-interactivity-area {
-    display:block; /* or inline-block */
-    width: 100%; padding: 7px 0; text-align:center; margin-left:10px; float:left;    
-    background:#fff; border-bottom:1px solid #fff;color:#000;
+.abu-xnat-interactivity-area {
+    border-bottom: 1px solid #e0e0e0;
+    margin-bottom: 2em;
 }
-div.abu-xnat-interactivity-area-contents {
-    display:inline; padding: 7px 0; text-align:right; margin-left:10px; margin-right:10px; float:right; width: 100%;    
+.abu-xnat-interactivity-area-contents {
+    padding: 7px 0;
 }
-div.abu-xnat-interactivity-area-sub {
-    display:inline; text-align:right; margin-left:10px; margin-right:10px; margin-top:0px; margin-bottom:5px; float:right; width: 100%;    
+.abu-xnat-interactivity-area-sub {
+    margin-bottom:5px;
 }
 
-.abu-done-button {
-    display:block; /* or inline-block */
-    width: 105px; padding: 7px 0; text-align:center; margin-left:10px; float:left;    
-    background:#1A75BB; border-bottom:1px solid #ddd;color:#fff;
-}
-.abu-done-button-hover {background:#0B6BE8;}
-.abu-done-button-focus {outline:1px dotted black;}
-
-.abu-process-button {
-    display:block; /* or inline-block */
-    width: 105px; padding: 7px 0; text-align:center; margin-left:10px; float:left;    
-    background:#1A75BB; border-bottom:1px solid #ddd;color:#fff;
-}
-.abu-process-button-hover {background:#0B6BE8;}
-.abu-process-button-focus {outline:1px dotted black;}
 
 .abu-upload-button {
-    display:block; /* or inline-block */
-    width: 105px; padding: 7px 0; text-align:center; margin-left:10px; float:left;    
-    background:#1A75BB; border-bottom:1px solid #ddd;color:#fff;
+    background:#f8f8f8 linear-gradient( #ffffff, #f0f0f0 );
+    border: 1px solid #848484;
+    border-radius: 5px;
+    cursor: pointer;
+    height: 18px; width: 105px;
+    margin-left:10px;
+    padding: 3px 0;
+    text-align:center;
+}
+.abu-upload-button:hover {
+    background:#e8e8e8;
+}
+.abu-upload-button:active {
+    background:#d8d8d8;
 }
-.abu-upload-button-hover {background:#0B6BE8;}
-.abu-upload-button-focus {outline:1px dotted black;}
 
 .abu-upload-drop-area {
     position:absolute; top:0; left:0; width:100%; height:100%; min-height: 70px; z-index:2;
@@ -94,17 +88,17 @@ div.abu-xnat-interactivity-area-sub {
 .abu-upload-duplicate {display:inline;color:#008800}
 .abu-upload-duplicate.abu-upload-duplicate-text {display:inline;color:#008800}
 
-.abu-uploader-instructions-sel {margin-bottom:0px; margin-right:10px; display:inline; float:right; background-color:#CCCCCC; border-style:solid; border-width:1px; width:auto; font-size:14px; font-weight: bold; padding:2px; }
+.abu-uploader-instructions-sel { float:right; padding:2px; }
 
 .abu-uploader-instructions {margin-bottom:10px; display:inline}
 
 .abu-return-floatmessage {position:absolute;z-index:10001;width:auto;cursor:pointer;background-color:#FFFFCC; }
 .abu-return-message {width:auto;cursor:pointer;background-color:#FFFFCC; }
 
-.abu-options-div { position: relative; float: left; width: 420px; margin-left: 10px; }
+.abu-options-div { margin-bottom: 1em; }
 .abu-options-cb {
-    display:block; /* or inline-block */
-    width: auto; padding: 7px 0; text-align:center; margin-left:10px; float:left; padding-top: 0px; padding-bottom: 0px;    
+    display: inline-block;
+    margin-right:10px;
 }
 
 .abu-progress { position:relative; display:inline-block; width:250px; height:15px; border: 1px solid #ddd; padding: 0px; border-radius: 0px; margin-left: 10px; }
@@ -112,6 +106,10 @@ div.abu-xnat-interactivity-area-sub {
 .abu-percent { position:absolute; display:inline-block; top:0px; left:48%; line-height: 15px; }
 .abu-status { position:relative; display:none; }
 
+.abu-config-param-div { width:120px; text-align:right; margin-right:10px; float:left; }
+.abu-config-param-grp { margin-bottom:15px; float:left; border:1px solid #CCCCCC; width: 600px; padding:5px; }
+.abu-config-param-row { width:100%; margin-bottom:5px; float:left; margin-right:15px; }
+
 .opaque {
 	opacity:0.5;
         -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
@@ -133,7 +131,7 @@ div.abu-xnat-interactivity-area-sub {
 
 .passParamText { margin-left: 30px; margin-right: 10px; }
 
-.interactivityAreaSpan { margin-left: 15px; margin-right: 10px; }
+.interactivityAreaSpan { margin-right: 10px; }
 
 #configUploadDiv { overflow-x: hidden; }
 
diff --git a/src/main/webapp/xdat-templates/layouts/Default.vm b/src/main/webapp/xdat-templates/layouts/Default.vm
index e19a81393c27a181b494a5c0915b6d310f8861d2..a4d925c346ea5880d9c5273626da61412da02611 100644
--- a/src/main/webapp/xdat-templates/layouts/Default.vm
+++ b/src/main/webapp/xdat-templates/layouts/Default.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 $navigation.setTemplate("/htmlOpen.vm")
 ## insert any template-specific <head> code here
 
@@ -8,7 +9,7 @@ $navigation.setTemplate("/bodyOpen.vm")
 <!-- path: xdat-templates/layouts/Default -->
 
             $navigation.setTemplate("/DefaultTop.vm")
-        #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar","true")))
+        #if($siteConfig.uiShowLeftBarBrowse)
                 $navigation.setTemplate("/DefaultLeft.vm")
         #end
             $navigation.setTemplate("/Breadcrumb.vm")
diff --git a/src/main/webapp/xdat-templates/macros/TurbineMacros.vm b/src/main/webapp/xdat-templates/macros/TurbineMacros.vm
index 221f0065299ee54b383c392cbf8b2fe53bc8db12..5d7955ff6e9f3f30ea973b0e05853e3fb81e09ca 100644
--- a/src/main/webapp/xdat-templates/macros/TurbineMacros.vm
+++ b/src/main/webapp/xdat-templates/macros/TurbineMacros.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="turbineUtils" type="org.nrg.xdat.turbine.utils.TurbineUtils" *#
 #* @vtlvariable name="user" type="org.nrg.xft.security.UserI" *#
 #* @vtlvariable name="tipText" type="java.util.List" *#
@@ -1636,7 +1637,7 @@ $!turbineUtils.escapeJS($s)
 
 ##Used to load globally scoped VM files and inject them into parent VMs.
 #macro(addGlobalCustomScreenJS $subFolder)
-	#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.debug-extension-points","false")))document.write("<div class='extension_js'>/templates/screens/$subFolder</div>")#end
+	#if($siteConfig.uiDebugExtensionPoints)document.write("<div class='extension_js'>/templates/screens/$subFolder</div>")#end
 	#foreach($screenProps in $turbineUtils.getTemplates($subFolder))
 		#set($templateFileName=$screenProps.getProperty("path"))
 		#if($user.isGuest())
@@ -1655,7 +1656,7 @@ $!turbineUtils.escapeJS($s)
 
 ##Used to load  VM files for specfic data-types and their extensions and inject them into parent VMs.
 #macro(addCustomScreenJS $dataType $subFolder)
-	#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.debug-extension-points","false")))document.write("<div class='extension_js'>/templates/screens/$dataType/$subFolder</div>")#end
+	#if($siteConfig.uiDebugExtensionPoints)document.write("<div class='extension_js'>/templates/screens/$dataType/$subFolder</div>")#end
 	#foreach($screenProps in $turbineUtils.getTemplates($dataType,$subFolder))
 		#set($templateFileName=$screenProps.getProperty("path"))
 		#if($user.isGuest())
@@ -1674,7 +1675,7 @@ $!turbineUtils.escapeJS($s)
 
 ##Used to load globally scoped VM files and inject them into parent VMs.
 #macro(addGlobalCustomScreens $subFolder)
-	#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.debug-extension-points","false")))<div class="extension">/templates/screens/$subFolder</div>#end
+	#if($siteConfig.uiDebugExtensionPoints)<div class="extension">/templates/screens/$subFolder</div>#end
 	#foreach($screenProps in $turbineUtils.getTemplates($subFolder))
 		#set($templateFileName=$screenProps.getProperty("path"))
 		#if($user.isGuest())
@@ -1693,7 +1694,7 @@ $!turbineUtils.escapeJS($s)
 
 ##Used to load  VM files for specfic data-types and their extensions and inject them into parent VMs.
 #macro(addCustomScreens $dataType $subFolder)
-	#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.debug-extension-points","false")))<div class="extension">/templates/screens/$dataType/$subFolder</div>#end
+	#if($siteConfig.uiDebugExtensionPoints)<div class="extension">/templates/screens/$dataType/$subFolder</div>#end
 	#foreach($screenProps in $turbineUtils.getTemplates($dataType,$subFolder))
 		#set($templateFileName=$screenProps.getProperty("path"))
 		#if($user.isGuest())
diff --git a/src/main/webapp/xdat-templates/screens/XDATScreen_report_xdat_user.vm b/src/main/webapp/xdat-templates/screens/XDATScreen_report_xdat_user.vm
index 89fc153433077c6912ef3443b18971016882de04..c30a5c1f4d7e234d76a29fe06d45d8c83949b45d 100644
--- a/src/main/webapp/xdat-templates/screens/XDATScreen_report_xdat_user.vm
+++ b/src/main/webapp/xdat-templates/screens/XDATScreen_report_xdat_user.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="userObject" type="org.nrg.xdat.security.XDATUser" *#
 #* @vtlvariable name="item" type="org.nrg.xft.XFTItem" *#
 #* @vtlvariable name="hasFailedLoginAttempts" type="java.lang.Boolean" *#
@@ -664,7 +665,7 @@ addGroup("$!groupid.getGroupid()");
 
 							  #set($groupCounter=0)
 
-                                #if($turbineUtils.toBoolean($siteConfig.getProperty("security.allow-data-admins","true")))
+                                #if($siteConfig.allowDataAdmins)
                                     <tr>
                                         <th align=left colspan="2"><br>Allow All Data Access: </th>
                                     </tr>
diff --git a/src/main/webapp/xnat-templates/layouts/Index.vm b/src/main/webapp/xnat-templates/layouts/Index.vm
index d3f68965fca42c141abbea4b840d0aa4d894ffcf..eac215ed5b7e63f7edaf69652ec6faa8052149bc 100644
--- a/src/main/webapp/xnat-templates/layouts/Index.vm
+++ b/src/main/webapp/xnat-templates/layouts/Index.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 $navigation.setTemplate("/htmlOpen.vm")
 ## insert any template-specific <head> code here
 
@@ -9,7 +10,7 @@ $navigation.setTemplate("/bodyOpen.vm")
 
         $navigation.setTemplate("/DefaultTop.vm")
 
-        #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar","true")))
+        #if($siteConfig.uiShowLeftBarBrowse)
 
                 <!-- DefaultLeft -->
                 $navigation.setTemplate("/DefaultLeft.vm")
diff --git a/src/main/webapp/xnat-templates/navigations/DefaultTop.vm b/src/main/webapp/xnat-templates/navigations/DefaultTop.vm
index 84e76b20d844ed63f4ddf9ac9b8740a26abc7321..fb24afed4a6aae6139fa758675c440c85571b04e 100644
--- a/src/main/webapp/xnat-templates/navigations/DefaultTop.vm
+++ b/src/main/webapp/xnat-templates/navigations/DefaultTop.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="user" type="org.nrg.xft.security.UserI" *#
 #* @vtlvariable name="timeLeft" type="java.lang.String" *#
 <!-- START: xnat-templates/navigations/DefaultTop.vm -->
@@ -216,10 +217,10 @@
 
 <div id="header" class="main_header"><div class="pad">
 
-    #if($siteConfig.getProperty("siteWideAlertStatus","")=="2")
-        <div class="$siteConfig.getProperty("siteWideAlertType","") headNotification hidden">
+    #if($siteConfig.siteWideAlertStatus == "2")
+        <div class="$siteConfig.siteWideAlertType" headNotification hidden">
             <span class="closeNotification"><img src="$content.getURI('images/close.gif')"></span>
-            <span>$siteConfig.getProperty("siteWideAlertMessage","")</span>
+            <span>$siteConfig.siteWideAlertMessage</span>
         </div>
 
         <script>
diff --git a/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm b/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
index e5e68f2b5ac9e0467d4643b2afe925209f65f818..f58cc7d7ed4d1be33d8a62bda92307aa44c25714 100755
--- a/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
+++ b/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="om" type="org.nrg.xdat.base.BaseElement" *#
 #* @vtlvariable name="project" type="java.lang.String" *#
 #* @vtlvariable name="requireReason" type="java.lang.String" *#
@@ -89,11 +90,11 @@ $navigation.setTemplate("/BaseJS.vm")
     // 'page' object is same as 'context' - easier to remember?
     XNAT.data.page = XNAT.data.context;
 
-    XNAT.app.showLeftBar = $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar","true"));
-    XNAT.app.showLeftBarProjects = $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar-projects","true"));
-    XNAT.app.showLeftBarFavorites = $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar-favorites","true"));
-    XNAT.app.showLeftBarSearch = $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar-search","true"));
-    XNAT.app.showLeftBarBrowse = $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-left-bar-browse","true"));
+    XNAT.app.showLeftBar = $siteConfig.uiShowLeftBarBrowse;
+    XNAT.app.showLeftBarProjects = $siteConfig.uiShowLeftBarProjects;
+    XNAT.app.showLeftBarFavorites = $siteConfig.uiShowLeftBarFavorites;
+    XNAT.app.showLeftBarSearch = $siteConfig.uiShowLeftBarSearch;
+    XNAT.app.showLeftBarBrowse = $siteConfig.uiShowLeftBarBrowse;
 
     window.available_elements = [];
 
diff --git a/src/main/webapp/xnat-templates/navigations/LeftBarOptions.vm b/src/main/webapp/xnat-templates/navigations/LeftBarOptions.vm
index 132fdc06efd7622b1fab226b6385abe5e5b3f797..297dfb032efb9ebb091231485b52066fd80a3441 100644
--- a/src/main/webapp/xnat-templates/navigations/LeftBarOptions.vm
+++ b/src/main/webapp/xnat-templates/navigations/LeftBarOptions.vm
@@ -1,3 +1,4 @@
-#if($siteConfig.getProperty("showapplet") && $siteConfig.getProperty("showapplet").equals("true"))
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
+#if($siteConfig.uiShowLeftBarAppletButton)
 <INPUT type="button" style="line-height:11px;font-size:11px;" VALUE="Launch Uploader" ONCLICK="window.location='$content.getURI("/app/template/LaunchUploadApplet.vm")';"/>
  #end
diff --git a/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm b/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm
index 41babb23fcda36252325fb0af436a9f930ab5292..6c27ffd2d23589ac3fa8ed2adeeade210927975e 100755
--- a/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm
+++ b/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <!-- search script -->
 <script type="text/javascript">
 function submitQuickSearch(){
@@ -42,7 +43,7 @@ function submitQuickSearch(){
 	</select>
 	<input id="searchValue" class="clean" name="searchValue" type="text" maxlength="40" size="20" value="$!field"/>
     <button type="button" id="search_btn" class="btn2" onclick="submitQuickSearch();">Go</button>
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-advanced-search","true")))
+#if($siteConfig.uiAllowAdvancedSearch)
 
     <script>
 
diff --git a/src/main/webapp/xnat-templates/screens/ForgotLogin.vm b/src/main/webapp/xnat-templates/screens/ForgotLogin.vm
index 9709c3996af30617a6b504af5a52972dc65cd9ac..2ea175ab895a280873f54e445657127124621d5f 100644
--- a/src/main/webapp/xnat-templates/screens/ForgotLogin.vm
+++ b/src/main/webapp/xnat-templates/screens/ForgotLogin.vm
@@ -1,14 +1,11 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 ##Copyright 2005 Harvard University / Howard Hughes Medical Institute (HHMI) All Rights Reserved
 #set ($template = $data.getTemplateInfo())
 $!template.setLayoutTemplate("Login.vm")
-#set ($siteId = $siteConfig.getProperty("siteId",""))
+#set ($siteId = $siteConfig.siteId)
 $page.setTitle("$siteId - Register")
 
 <div id="login_area">
-##    #if($siteConfig.getProperty("siteWideAlertStatus","")!="0")
-##        <div class="$siteConfig.getProperty("siteWideAlertType","")" style="margin-bottom: 2em;"><strong>$siteConfig.getProperty("siteWideAlertMessage","")</strong></div>
-##    #end
-
     <div id="register_box_container">
         <div id="register_box">
             <form name="form2" method="post" action="$link.setAction("XDATForgotLogin")" class="friendlyForm" id="register_form">
diff --git a/src/main/webapp/xnat-templates/screens/Index.vm b/src/main/webapp/xnat-templates/screens/Index.vm
index 92190d5400f471322cb49b63b617ebae5ac6e44f..9c2e251eb7519c73d1d12c76099c39f93ec220ea 100644
--- a/src/main/webapp/xnat-templates/screens/Index.vm
+++ b/src/main/webapp/xnat-templates/screens/Index.vm
@@ -7,9 +7,9 @@
 #* @vtlvariable name="content" type="org.apache.turbine.services.pull.tools.ContentTool" *#
 #set ($template = $data.getTemplateInfo())
 #if($data.getParameters().getString("login").equals("true"))
-    $!template.setLayoutTemplate($siteConfig.getProperty("siteLandingLayout","/Index.vm"))
+    $!template.setLayoutTemplate($siteConfig.siteLandingLayout)
 #else
-    $!template.setLayoutTemplate($siteConfig.getProperty("siteHomeLayout","/Index.vm"))
+    $!template.setLayoutTemplate($siteConfig.siteHomeLayout)
 #end
 #if ($data.message)
 <div class="note" id="messageToDisplay">$data.message</div><br>
@@ -28,14 +28,14 @@
 <div style="line-height:6px">&nbsp;</div>
 #end
 #if($data.getParameters().getString("login").equals("true"))
-    #if($turbineUtils.resourceExists($siteConfig.getProperty("siteLoginLanding", "/screens/QuickSearch.vm")))
-        #parse($siteConfig.getProperty("siteLoginLanding","/screens/QuickSearch.vm"))
+    #if($turbineUtils.resourceExists($siteConfig.siteLoginLanding))
+        #parse($siteConfig.siteLoginLanding)
     #else
         <div>Custom site login landing page cannot be found!<br/><br/>Contact the site administrator.</div>
     #end
 #else
-    #if($turbineUtils.resourceExists($siteConfig.getProperty("siteHome","/screens/QuickSearch.vm")))
-        #parse($siteConfig.getProperty("siteHome","/screens/QuickSearch.vm"))
+    #if($turbineUtils.resourceExists($siteConfig.siteHome))
+        #parse($siteConfig.siteHome)
     #else
         <div>Custom site home page cannot be found!<br/><br/>Contact the site administrator.</div>
     #end
diff --git a/src/main/webapp/xnat-templates/screens/Login.vm b/src/main/webapp/xnat-templates/screens/Login.vm
index 5aa1641195087012a4533a03f30c4f23676d7a38..e35182a0f316e83fa1548895678c421f68fea50e 100644
--- a/src/main/webapp/xnat-templates/screens/Login.vm
+++ b/src/main/webapp/xnat-templates/screens/Login.vm
@@ -8,7 +8,7 @@
 #* @vtlvariable name="ui" type="org.apache.turbine.services.pull.util.UIManager" *#
 #* @vtlvariable name="data" type="org.apache.turbine.util.RunData" *#
 #* @vtlvariable name="link" type="org.apache.turbine.services.pull.tools.TemplateLink" *#
-#set ($siteId = $siteConfig.getProperty("siteId",""))
+#set ($siteId = $siteConfig.siteId)
 $page.setTitle("$siteId - Please Login")
     $page.setBgColor($ui.bgcolor)
     $page.addAttribute("onLoad", "document.getElementById('username').focus();")
@@ -28,13 +28,31 @@ $page.setTitle("$siteId - Please Login")
     </div>
     <![endif]-->
 
-#if($siteConfig.getProperty("siteWideAlertStatus","")!="0" && $siteConfig.getProperty("siteWideAlertStatus","")!="off")
-    <div class="$siteConfig.getProperty("siteWideAlertType","")" style="margin-bottom: 2em;" data-status="$siteConfig.getProperty("siteWideAlertStatus","")">$siteConfig.getProperty("siteWideAlertMessage","")</div>
+#if($siteConfig.siteWideAlertStatus != "0" && $siteConfig.siteWideAlertStatus != "off")
+    <div class="$siteConfig.siteWideAlertType" style="margin-bottom: 2em;" data-status="$siteConfig.siteWideAlertStatus">$siteConfig.siteWideAlertMessage</div>
 #end
 
 #if($turbineUtils.GetPassedParameter("par",$data))
 ## create custom layout when parameters are passed to the login page from an external link.
 
+    <div id="login_welcome">
+        #if($siteConfig.siteDescriptionType == "Text")
+            <div id="siteDescription"></div>
+            <textarea id="siteDescriptionMd" style="display: none;">$siteConfig.siteDescriptionText</textarea>
+            <script>
+                var mdtext = jq('#siteDescriptionMd').text();
+                var markedText = marked(mdtext);
+                jq('#siteDescription').html(markedText);
+            </script>
+        #else
+            #if($turbineUtils.resourceExists($siteConfig.siteDescriptionPage))
+                #parse($siteConfig.siteDescriptionPage)
+            #else
+                <p>Custom site description page not found!</p>
+            #end
+        #end
+    </div>
+
     #if($data.getMessage())
     <div class="warning">
         <p><strong>Note: </strong><br />$data.getMessage()</p>
@@ -51,7 +69,7 @@ $page.setTitle("$siteId - Please Login")
     <div id="login_box_container" class="hidden">
         <h3>Existing User Login <span class="btn btn-xs btn-inline right" onclick="showRegistration()">Register?</span> </h3>
         <div class="message">
-            <p><strong>Note: </strong> It looks like you received an invitation to join this site via email. Logging in here will accept that invitation and tie it with your existing $siteConfig.getProperty("siteId","") account. </p>
+            <p><strong>Note: </strong> It looks like you received an invitation to join this site via email. Logging in here will accept that invitation and tie it with your existing $siteConfig.siteId account. </p>
             <p>&nbsp;</p>
             <p>Not what you want? <a href="$content.getURI('/app/template/Login.vm')"><strong>Default Log In Page</strong></a></p>
         </div>
@@ -129,7 +147,26 @@ $page.setTitle("$siteId - Please Login")
 #else
 ## standard layout for login page
 
-    <div id="login_box" >
+    <div id="login_welcome">
+        #if($siteConfig.siteDescriptionType == "Text")
+            <div id="siteDescription"></div>
+            <div id="siteDescriptionMd" style="display: none;">$siteConfig.siteDescriptionText</div>
+            <script>
+                var mdtext = jq('#siteDescriptionMd').text();
+                var markedText = marked(mdtext);
+                jq('#siteDescription').html(markedText);
+            </script>
+        #else
+            #if($turbineUtils.resourceExists($siteConfig.siteDescriptionPage))
+                #parse($siteConfig.siteDescriptionPage)
+            #else
+                <p>Custom site description page not found!</p>
+            #end
+        #end
+    </div>
+
+    <div id="login_box">
+
 
         #if($data.getMessage())
         <div class="message" style="border-bottom: 0">
diff --git a/src/main/webapp/xnat-templates/screens/PrearchiveDetails.vm b/src/main/webapp/xnat-templates/screens/PrearchiveDetails.vm
index a5f2913b7b2f897e4182bb85813c1d0b6365f1e0..2cc3a97983733c617aa87182eb42aaa47b545f6e 100644
--- a/src/main/webapp/xnat-templates/screens/PrearchiveDetails.vm
+++ b/src/main/webapp/xnat-templates/screens/PrearchiveDetails.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #if($popup=="true")
 	#set ($template = $data.getTemplateInfo())
 	$!template.setLayoutTemplate("Popup.vm")
@@ -244,10 +245,10 @@ dd.valMessage 	{	width:630px;	}
 
         #if($allowArchive)
 	//validation CODES that are not allowed to be overidden
-	XNAT.app.validator.fail_merge_on=$siteConfig.getProperty("security.fail_merge_on","[]");
+	XNAT.app.validator.fail_merge_on=$siteConfig.formattedFailMergeOn;
 	XNAT.app.validator.xsiType="$session.getXSIType()";
 	
-	XNAT.app.archiveValidator.fail_merge_on=$siteConfig.getProperty("security.fail_merge_on","[]");
+	XNAT.app.archiveValidator.fail_merge_on=$siteConfig.formattedFailMergeOn;
 	XNAT.app.archiveValidator.xsiType="$session.getXSIType()";
 	#if($url)
 		XNAT.app.archiveValidator.url="$!url";
diff --git a/src/main/webapp/xnat-templates/screens/Register.vm b/src/main/webapp/xnat-templates/screens/Register.vm
index f4da09b04f289ffa920b7f34e8fedd56de773fe2..257321a9c8f067aadfbe4670a5478292bbd6f6be 100644
--- a/src/main/webapp/xnat-templates/screens/Register.vm
+++ b/src/main/webapp/xnat-templates/screens/Register.vm
@@ -10,16 +10,16 @@ $page.setTitle("Register A New User Account")
 $page.setBgColor($ui.bgcolor)
 
 <div id="login_area">
-    #if($siteConfig.getProperty("siteWideAlertStatus","")!="0")
-        <div class="$siteConfig.getProperty("siteWideAlertType","")" style="margin-bottom: 2em;"><strong>$siteConfig.getProperty("siteWideAlertMessage","")</strong></div>
+    #if($siteConfig.siteWideAlertStatus != "0" && $siteConfig.siteWideAlertStatus != "off")
+        <div class="$siteConfig.siteWideAlertType" style="margin-bottom: 2em;"><strong>$siteConfig.siteWideAlertMessage</strong></div>
     #end
 
 #*    <div id="login_welcome">
-        #if($siteConfig.getProperty("siteDescriptionType","")=="Text")
-            <div>$siteConfig.getProperty("siteDescriptionText","")</div>
+        #if($siteConfig.siteDescriptionType == "Text")
+            <div>$siteConfig.siteDescriptionText</div>
         #else
-            #if($turbineUtils.resourceExists($siteConfig.getProperty("siteDescriptionPage","/screens/site_description.vm")))
-                <div>#parse($siteConfig.getProperty("siteDescriptionPage","/screens/site_description.vm"))</div>
+            #if($turbineUtils.resourceExists($siteConfig.siteDescriptionPage))
+                <div>#parse($siteConfig.siteDescriptionPage)</div>
             #else
                 <div>Custom site description page not found!</div>
             #end
diff --git a/src/main/webapp/xnat-templates/screens/Scripts.vm b/src/main/webapp/xnat-templates/screens/Scripts.vm
index 318fb4d53c488b40d265bfad0b0565a47df5176c..04d1a09e82ff3d91da91cec50501ef775b9defe5 100644
--- a/src/main/webapp/xnat-templates/screens/Scripts.vm
+++ b/src/main/webapp/xnat-templates/screens/Scripts.vm
@@ -451,7 +451,7 @@
                 </tr>
             </table>
             <br>
-            <div class="editor-wrapper" style="width:840px;height:440px;position:relative;">
+            <div class="editor-wrapper" style="width:840px;height:320px;position:relative;">
                 <!-- the '.editor-content' div gets replaced when the script content is loaded -->
                 <div class="editor-content" style="position:absolute;top:0;right:0;bottom:0;left:0;border:1px solid #ccc;"></div>
             </div>
diff --git a/src/main/webapp/xnat-templates/screens/XDATScreen_uploaded_xnat_imageSessionData.vm b/src/main/webapp/xnat-templates/screens/XDATScreen_uploaded_xnat_imageSessionData.vm
index 97d4f8bb8f4e6b58c0dab8235331e9da34757ac8..80699ab851831c5881e1bf5100daf128d0694e33 100644
--- a/src/main/webapp/xnat-templates/screens/XDATScreen_uploaded_xnat_imageSessionData.vm
+++ b/src/main/webapp/xnat-templates/screens/XDATScreen_uploaded_xnat_imageSessionData.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #set($target=$content.getURI("/REST/services/archive"))
 #set($allowDataDeletion="false")
 #set($isUpload="true")
@@ -57,7 +58,7 @@ dd.valMessage 	{	width:630px;	}
 <script type="text/javascript" src="$content.getURI("scripts/imageSessionData/archive.js")"></script>
 <script>	
 	//validation CODES that are not allowed to be overidden
-	XNAT.app.archiveValidator.fail_merge_on=$siteConfig.getProperty("security.fail_merge_on","[]");
+	XNAT.app.archiveValidator.fail_merge_on=$siteConfig.formattedFailMergeOn;
 	XNAT.app.archiveValidator.xsiType="$om.getXSIType()";
 	#if($src)
 		XNAT.app.archiveValidator.url="$!src";
diff --git a/src/main/webapp/xnat-templates/screens/register_box.vm b/src/main/webapp/xnat-templates/screens/register_box.vm
index 8365ce8fc9ddc9a6305882ea2b2999bf680ab7fe..eaf2f950c61a8d58e5f18b827a7b3f7a252f1e7e 100644
--- a/src/main/webapp/xnat-templates/screens/register_box.vm
+++ b/src/main/webapp/xnat-templates/screens/register_box.vm
@@ -50,7 +50,7 @@
         <label for="lab">Study Site or Lab</label>
         <input type="text" name="lab" value="$!lab">
     </p>
-    #if($siteConfig.getUiAllowNewUserComments())
+    #if($siteConfig.uiAllowNewUserComments)
         <hr/>
         <p>Please include a brief description of how you plan to use this site.</p>
         <p>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Automation.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Automation.vm
new file mode 100644
index 0000000000000000000000000000000000000000..1396e02fbcbb15c3cffa7bee9246b9d62729a115
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Automation.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 80 -->
+
+<li><a href="${SITE_ROOT}/app/template/Scripts.vm">Automation</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/DataTypes.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/DataTypes.vm
new file mode 100644
index 0000000000000000000000000000000000000000..61664a8a2a727664bdfe8e8732c1da746d1dd2e3
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/DataTypes.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 50 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_dataTypes.vm">Data Types</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm
index 68db2d8eafd0818fd69fb4611def0ee025ef3baf..5757cb5dcf47f478cc1f2a585cd016696990fcd1 100644
--- a/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm
@@ -1,10 +1,82 @@
 <!-- Sequence: 10 -->
-##<li><a href="/page/admin/">Site Administration</a></li>
-<li><a href="${SITE_ROOT}/app/template/Page.vm?view=admin">Site Administration</a></li>
-<li><a href="$link.setPage("XDATScreen_admin.vm")">Users</a></li>
-<li><a href="$link.setPage("XDATScreen_groups.vm")">Groups</a></li>
-<li><a href="$link.setPage("XDATScreen_dataTypes.vm")">Data Types</a></li>
-<li><a href="$link.setPage("XDATScreen_email.vm")">Email</a></li>
-<li><a href="$link.setPage("XDATScreen_manage_pipeline.vm")">Pipelines</a></li>
-<li><a href="$link.setPage("Scripts.vm")">Automation</a></li>
-<li><a href="$link.setPage("XDATScreen_admin_options.vm")">More...</a></li>
+
+<!-- xnat-templates/screens/topBar/Administer/Default.vm -->
+
+####<li><a href="/page/admin/">Site Administration</a></li>
+##<li><a href="${SITE_ROOT}/app/template/Page.vm?view=admin">Site Administration</a></li>
+##<li id="view-plugin-settings" class="hidden"><a href="${SITE_ROOT}/app/template/Page.vm?view=admin/plugins">Plugin Settings</a></li>
+##<li><a href="$link.setPage("XDATScreen_admin.vm")">Users</a></li>
+##<li><a href="$link.setPage("XDATScreen_groups.vm")">Groups</a></li>
+##<li><a href="$link.setPage("XDATScreen_dataTypes.vm")">Data Types</a></li>
+##<li><a href="$link.setPage("XDATScreen_email.vm")">Email</a></li>
+##<li><a href="$link.setPage("XDATScreen_manage_pipeline.vm")">Pipelines</a></li>
+##<li><a href="$link.setPage("Scripts.vm")">Automation</a></li>
+##<li><a href="$link.setPage("XDATScreen_admin_options.vm")">More...</a></li>
+##
+##<script src="${SITE_ROOT}/scripts/xnat/admin/pluginSettings.js"></script>
+##
+##<script>
+##    XNAT.xhr.get(XNAT.url.restUrl('/xapi/plugins'), function(plugins){
+##
+##        console.log('/xapi/plugins response');
+##        console.log(plugins);
+##
+##        var hideMenuItem = true;
+##
+##        if (!isEmpty(plugins)) {
+##
+##            var setPaths = function(names){
+##                var paths = [];
+##                [].concat(names).forEach(function(name){
+##                    paths.push(name + '/siteSettings');
+##                    paths.push(name + '/admin');
+##                });
+##                return paths;
+##            };
+##
+##            // calling forOwn() returns the key names
+##            var pluginNames = forOwn(plugins);
+##            var pluginSettingsPaths = setPaths(pluginNames);
+##
+##            function getPluginSpawnerElements(path){
+##                var _url = XNAT.url.restUrl('/xapi/spawner/resolve/' + path)
+##                return XNAT.xhr.get(_url);
+##            }
+##
+##            function lookForSettings(i) {
+##                if (i === pluginSettingsPaths.length){
+##                    // console.log("couldn't do it");
+##                    return false;
+##                }
+##                // recursively try to get settings at different places
+##                getPluginSpawnerElements(pluginSettingsPaths[i])
+##                        .done(function(){
+##                            $('#view-plugin-settings').removeClass('hidden');
+##                        })
+##                        .fail(function(){
+##                            lookForSettings(++i)
+##                        });
+##            }
+##
+##            // do the stuff
+##            lookForSettings(0);
+##
+##//            forOwn(plugins, function(name, obj){
+##//                if (hideMenuItem) return;
+##//                getPluginSpawnerElements(name + '/siteSettings')
+##//                        .done(function(){
+##//                            showMenuItem = true;
+##//                            $('#view-plugin-settings').removeClass('hidden');
+##//                        })
+##//                        .fail(function(){
+##//                            getPluginSpawnerElements(name + '/admin')
+##//                                    .done(function(){
+##//                                        showMenuItem = true;
+##//                                        $('#view-plugin-settings').removeClass('hidden');
+##//                                    })
+##//                        });
+##//            });
+##        }
+##
+##    });
+##</script>
\ No newline at end of file
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Email.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Email.vm
new file mode 100644
index 0000000000000000000000000000000000000000..10d03d5979f4dcd039c7e40b5d063d1986201ea5
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Email.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 60 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_email.vm">Email</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Groups.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Groups.vm
new file mode 100644
index 0000000000000000000000000000000000000000..0886e2dd9f9b72e355972cc1bb02a5ac62d53945
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Groups.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 40 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_groups.vm">Groups</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/More.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/More.vm
new file mode 100644
index 0000000000000000000000000000000000000000..a7af17941e0bd2fe8a7d01b25ca4796f7dcea7bf
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/More.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 90 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_admin_options.vm">More...</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Pipelines.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Pipelines.vm
new file mode 100644
index 0000000000000000000000000000000000000000..2abb6acc66ec6346b3520dadbbdb0e793b607394
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Pipelines.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 70 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_manage_pipeline.vm">Pipelines</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/PluginSettings.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/PluginSettings.vm
new file mode 100644
index 0000000000000000000000000000000000000000..61631d4243c994f67d6a0ffe11f7a05bec31f21f
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/PluginSettings.vm
@@ -0,0 +1,5 @@
+<!-- Sequence: 20 -->
+<li id="view-plugin-settings" class="hidden">
+    <a href="${SITE_ROOT}/app/template/Page.vm?view=admin/plugins">Plugin Settings</a>
+</li>
+<script src="${SITE_ROOT}/scripts/xnat/admin/pluginSettings.js"></script>
\ No newline at end of file
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/SiteAdmin.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/SiteAdmin.vm
new file mode 100644
index 0000000000000000000000000000000000000000..d5b31ceeb8ad1373c03a05b39ff5d85ca31dda80
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/SiteAdmin.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 10 -->
+##<li><a href="/page/admin/">Site Administration</a></li>
+<li><a href="${SITE_ROOT}/app/template/Page.vm?view=admin">Site Administration</a></li>
\ No newline at end of file
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Users.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Users.vm
new file mode 100644
index 0000000000000000000000000000000000000000..ebfc04f4be119c8b9386672f8e52ad9c1cd24b8c
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Users.vm
@@ -0,0 +1,3 @@
+<!-- Sequence: 30 -->
+
+<li><a href="${SITE_ROOT}/app/template/XDATScreen_admin.vm">Users</a></li>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/Browse/Default.vm b/src/main/webapp/xnat-templates/screens/topBar/Browse/Default.vm
index 3754ef34ae351565d59d732d0ab8b2c8b36d8dca..d55aacf299e68a2b19093a94c473115e22a9f61b 100644
--- a/src/main/webapp/xnat-templates/screens/topBar/Browse/Default.vm
+++ b/src/main/webapp/xnat-templates/screens/topBar/Browse/Default.vm
@@ -1,10 +1,11 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <!-- Sequence: 10 -->
 
 <!-- Browse/Default -->
     <li class="hidden"><a href="#BrowseProjects">Projects</a>
         <ul id="browse-projects">
             <!-- Sequence: 10 -->
-            #if($siteConfig.getUiAllowNonAdminProjectCreation() || $data.getSession().getAttribute("userHelper").canCreate("xnat:subjectData/project"))
+            #if($siteConfig.uiAllowNonAdminProjectCreation || $data.getSession().getAttribute("userHelper").canCreate("xnat:subjectData/project"))
                 <li class="create-project hidden"><a href="$link.setPage("XDATScreen_add_xnat_projectData.vm")">Create $displayManager.getSingularDisplayNameForProject()</a></li>
             #end
         </ul>
diff --git a/src/main/webapp/xnat-templates/screens/topBar/New/Default.vm b/src/main/webapp/xnat-templates/screens/topBar/New/Default.vm
index 576074f3d5f167ce7063ca63200deec63b278502..3cb3b827b47fab26a54515be2c470386170bd165 100644
--- a/src/main/webapp/xnat-templates/screens/topBar/New/Default.vm
+++ b/src/main/webapp/xnat-templates/screens/topBar/New/Default.vm
@@ -5,7 +5,7 @@
 #* @vtlvariable name="displayManager" type="org.nrg.xdat.display.DisplayManager" *#
 #* @vtlvariable name="project" type="org.nrg.xdat.om.XnatProjectdata" *#
 <!-- Sequence: 10 -->
-    #if ($turbineUtils.isSiteAdmin($user) || $siteConfig.getUiAllowNonAdminProjectCreation() || $data.getSession().getAttribute("userHelper").canCreate("xnat:subjectData/project"))
+    #if ($turbineUtils.isSiteAdmin($user) || $siteConfig.uiAllowNonAdminProjectCreation || $data.getSession().getAttribute("userHelper").canCreate("xnat:subjectData/project"))
     <li><a href="$link.setPage("XDATScreen_add_xnat_projectData.vm")">$displayManager.getSingularDisplayNameForProject()</a></li>
     #end
     <!-- -->
diff --git a/src/main/webapp/xnat-templates/screens/xnat_ctSessionData/edit/scans.vm b/src/main/webapp/xnat-templates/screens/xnat_ctSessionData/edit/scans.vm
index 7e2e552f5739d7774427f4b7f4f9521c8eb7e741..8bba8a8f82e8cebe00ca0ddd61561ef7b0ab108c 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_ctSessionData/edit/scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_ctSessionData/edit/scans.vm
@@ -5,7 +5,7 @@
 #* @vtlvariable name="om" type="org.nrg.xdat.om.XnatCtsessiondata" *#
 #* @vtlvariable name="scan" type="org.nrg.xdat.om.XnatImagescandata" *#
 <!-- BEGIN /xnat-templates/screens/xnat_ctSessionData/edit/scans.vm -->
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 <DIV class="edit_header1" style="margin-bottom:16px">Scans &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 <input type="button" value="Add Scan" onclick="addScan(this)"/>
 </DIV>
@@ -112,7 +112,7 @@
   });
       
       
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
   
   if(window.scanSet.scans.length==0){
     for(var newC=0;newC<5;newC++){
@@ -122,7 +122,7 @@
   }
 #end
   
-XNAT.app.sTMod=$turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-type-modification","true"))
+XNAT.app.sTMod=$siteConfig.uiAllowScanTypeModification;
        
   window.scanSet.onLoad.fire();
 </script>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_experimentData/actionsBox/ManageFiles.vm b/src/main/webapp/xnat-templates/screens/xnat_experimentData/actionsBox/ManageFiles.vm
index c3b4e3314981dec0167bd32f42e602a781c86b4d..232b1aefbf223e095ff638a85bc1e3e6969be164 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_experimentData/actionsBox/ManageFiles.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_experimentData/actionsBox/ManageFiles.vm
@@ -1,5 +1,6 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <!-- Sequence: 5 -->
-#if($user.checkFeature($om,"manage_files") && $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-manage-files","true")))
+#if($user.checkFeature($om,"manage_files") && $siteConfig.uiShowManageFiles)
      <li class="yuimenuitem">
 			<A href="" ONCLICK="showFiles();return false;"><div class="ic">#if($actionObject.hasImage())<img border="0" src="$content.getURI("scripts/yui/build/treeview/assets/img/folders/cf.gif")"/>#else&nbsp;#end</div><div class="ic_spacer">&nbsp;</div>Manage Files</A>
 	 </li>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/edit/scans.vm b/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/edit/scans.vm
index 88cbb22a7b5fb4fa2a69255f2618439e803e3859..0e427585ccb8d59f9a5d36420e5bfff80b4c4376 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/edit/scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/edit/scans.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="user" type="org.nrg.xdat.security.XDATUser" *#
 #* @vtlvariable name="scan" type="org.nrg.xdat.om.XnatImagescandata" *#
 <!-- BEGIN /xnat-templates/screens/xnat_imageSessionData/edit/scans.vm -->
@@ -6,7 +7,7 @@ XNAT.app.scans={};
 </script>
 <DIV class="edit_header1" style="margin-bottom:16px">Scans &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 <input type="button" value="Add Scan" onclick="addScan(this)"/>
 #end
 </DIV>
@@ -113,7 +114,7 @@ XNAT.app.scans={};
   });
       
       
-  #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+  #if($siteConfig.uiAllowScanAddition)
       if(window.scanSet.scans.length==0){
         for(var newC=0;newC<5;newC++){
           var tempScan = window.classMapping.newInstance(getDefaultScanXSIType());
@@ -122,7 +123,7 @@ XNAT.app.scans={};
       }
   #end
   
-  XNAT.app.sTMod=$turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-type-modification","true"))
+  XNAT.app.sTMod=$siteConfig.uiAllowScanTypeModification;
       
   window.scanSet.onLoad.fire();
 </script>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/xnat_imageSessionData_scans.vm b/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/xnat_imageSessionData_scans.vm
index c572cf802bcfe31cdedd00f7e928e8197f461805..59371575ee5a862c931aa4946ce16ccaa30c13ce 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/xnat_imageSessionData_scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_imageSessionData/xnat_imageSessionData_scans.vm
@@ -21,7 +21,7 @@
     <tr style="border:none;">
         <th class="underscore" align=left>Scan</th>
         <th class="underscore" align=left>Type</th>
-        #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.display-series-description","true")))
+        #if($siteConfig.uiDebugExtensionPoints)
             <th class="underscore" align=left>Series Desc</th>
             #set($fileIndex="4")
         #else
@@ -45,7 +45,7 @@
                 </a>
             </td>
             <td border=0 align=left NOWRAP>$!scan.getProperty("type")</td>
-            #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.display-series-description","true")))
+            #if($siteConfig.uiDebugExtensionPoints)
                 <td border=0 align=left NOWRAP>$!scan.getProperty("series_description")</td>
             #end
             <td border=0 class="quality-$!scan.getProperty("quality")" style="font-weight:bold; text-align:left;"  NOWRAP>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_mrSessionData/edit/scans.vm b/src/main/webapp/xnat-templates/screens/xnat_mrSessionData/edit/scans.vm
index 81bfd64b6ae52dbdfb890a7ef2e8d782ccfa49c9..95c7d29f209ded7ae29726f0b1508e846ac64fd9 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_mrSessionData/edit/scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_mrSessionData/edit/scans.vm
@@ -1,7 +1,8 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="scan" type="org.nrg.xdat.om.XnatImagescandata" *#
 #* @vtlvariable name="user" type="org.nrg.xdat.security.XDATUser" *#
 <!-- BEGIN /xnat-templates/screens/xnat_mrSessionData/edit/scans.vm -->
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 <DIV class="edit_header1" style="margin-bottom:16px">Scans &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 <input type="button" value="Add Scan" onclick="addScan(this)"/>
 </DIV>
@@ -116,7 +117,7 @@
   });
 
 
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 
   if(window.scanSet.scans.length==0){
     for(var newC=0;newC<5;newC++){
@@ -126,7 +127,7 @@
   }
 #end
 
-XNAT.app.sTMod=$turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-type-modification","true"))
+XNAT.app.sTMod=$siteConfig.uiAllowScanTypeModification;
 
   window.scanSet.onLoad.fire();
 </script>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_petSessionData/edit/scans.vm b/src/main/webapp/xnat-templates/screens/xnat_petSessionData/edit/scans.vm
index 4e34b8168b3cb980260996272c45178329fa0de9..783a46b89beec978611312c6d94d50ac5bdf5e9d 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_petSessionData/edit/scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_petSessionData/edit/scans.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="scan" type="org.nrg.xdat.om.XnatImagescandata" *#
 #* @vtlvariable name="user" type="org.nrg.xdat.security.XDATUser" *#
 <!-- BEGIN /xnat-templates/screens/xnat_petSessionData/edit/scans.vm -->
@@ -7,7 +8,7 @@
 #end
 <DIV class="edit_header1" style="margin-bottom:16px">Scans &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 <input type="button" value="Add Scan" onclick="addScan(this)"/>
 #end
 </DIV>
@@ -107,7 +108,7 @@
     listing.render();
   });
       
-  #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+  #if($siteConfig.uiAllowScanAddition)
       if(window.scanSet.scans.length==0){
         for(var newC=0;newC<2;newC++){
           var tempScan = window.classMapping.newInstance("xnat:petScanData");
@@ -116,7 +117,7 @@
       }
     #end
 
-  XNAT.app.sTMod=$turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-type-modification","true"))
+  XNAT.app.sTMod=$siteConfig.uiAllowScanTypeModification
       
   window.scanSet.onLoad.fire();
 </script>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_petmrSessionData/edit/scans.vm b/src/main/webapp/xnat-templates/screens/xnat_petmrSessionData/edit/scans.vm
index 63de28f4babfbfac8b076542fe6b8e8283fbb782..5f333bf2f3f7258a5567dad52bb56b33a8856547 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_petmrSessionData/edit/scans.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_petmrSessionData/edit/scans.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #* @vtlvariable name="scan" type="org.nrg.xdat.om.XnatImagescandata" *#
 #* @vtlvariable name="user" type="org.nrg.xdat.security.XDATUser" *#
 <!-- BEGIN /xnat-templates/screens/xnat_petmrSessionData/edit/scans.vm -->
@@ -6,7 +7,7 @@
     #set($petscanTypes = $data.getSession().getAttribute("userHelper").getQueryResultsAsArrayList("select DISTINCT isd.type,isd.type from xnat_petscandata mr LEFT JOIN xnat_imagescandata isd ON mr.xnat_imagescandata_id=isd.xnat_imagescandata_id"))
 #end
 
-#if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-addition","true")))
+#if($siteConfig.uiAllowScanAddition)
 <DIV class="edit_header1" style="margin-bottom:16px">Scans &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
     <input type="button" value="Add Scan" onclick="addScan(this)"/>
 </DIV>
@@ -118,7 +119,7 @@
         listing.render();
     });
 
-    XNAT.app.sTMod=$turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-scan-type-modification","true"))
+    XNAT.app.sTMod=$siteConfig.uiAllowScanTypeModification;
 
     window.scanSet.onLoad.fire();
 </script>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_projectData/actionsBox/ManageFiles.vm b/src/main/webapp/xnat-templates/screens/xnat_projectData/actionsBox/ManageFiles.vm
index 3bc74200b3c5fe92756aa1a987d42893d6765a0e..82f6373c64d789072756545b93b557f97705cbae 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_projectData/actionsBox/ManageFiles.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_projectData/actionsBox/ManageFiles.vm
@@ -1,5 +1,6 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <!-- BEGIN xnat-templates/screens/xnat_projectData/actionsBox/ManageFiles.vm -->
-#if($user.checkFeature($om,"manage_files") && $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-project-manage-files","true")))
+#if($user.checkFeature($om,"manage_files") && $siteConfig.uiShowProjectManageFiles)
      <li class="yuimenuitem">
 			<A href="" ONCLICK="window.viewer.render();return false;">
 				<div class="ic">
diff --git a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_details.vm b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_details.vm
index be2031d6d1eda25e611fc1dc3936340ed7bf955c..5da17ef6daca9bb9281152f9ec7f8e76afedca57 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_details.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_details.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <table>
     <tr>
         <th align="left">ID:</th>
@@ -53,8 +54,7 @@
     "search_element","xnat:projectData").addPathInfo("search_field","xnat:projectData.ID").addPathInfo(
     "search_value","$!{project.getId()}").addPathInfo("popup","$!popup")">Edit Details</A>
     #if($data.getSession().getAttribute(
-        "userHelper").canDelete($project) && $turbineUtils.toBoolean($siteConfig.getProperty(
-        "UI.allow-project-delete","true")))
+        "userHelper").canDelete($project) && $siteConfig.uiAllowProjectDelete)
     <A ID="button3" href="$link.setAction("XDATActionRouter").addPathInfo("xdataction","delete").addPathInfo(
         "search_element","xnat:projectData").addPathInfo("search_field","xnat:projectData.ID").addPathInfo(
         "search_value","$!{project.getId()}").addPathInfo("popup","$!popup")">Delete</A>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_manage.vm b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_manage.vm
index d84106629a122b139e513f1aacb605125d5a63fe..8e085e28af29b42699d007960f15d4ed3c40f718 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_manage.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_manage.vm
@@ -184,7 +184,7 @@
 </div>
 
 <div id="accordion">
-    #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-quarantine","true")))
+    #if($siteConfig.uiAllowQuarantine)
         <h3 class="active">Define Quarantine Settings</h3>
         <div class="active">
             <table id="quarantine_table">
@@ -227,7 +227,7 @@
             </script>
         </div>
     #end
-    #if($turbineUtils.toBoolean($siteConfig.getProperty("project.allow-auto-archive","true")))
+    #if($siteConfig.projectAllowAutoArchive)
         <h3>Define Prearchive Settings</h3>
         <div>
             <table id="prearchive_table">
@@ -431,7 +431,7 @@
             filtersGet.get();
         </script>
     </div>
-    #if($turbineUtils.toBoolean($siteConfig.getProperty("enableProjectAppletScript", "false")))
+    #if($siteConfig.enableProjectAppletScript)
         <h3>Applet Configuration Script</h3>
         <div>
             <table id="applet_script_table">
diff --git a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_management.vm b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_management.vm
index 9fbd5cf16fd16d767b535b6d58e12c7dfa831b96..612fe53b60664413926a7e9f5db53c9f04d2a0f7 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_management.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_projectData/xnat_projectData_summary_management.vm
@@ -2,8 +2,7 @@
 #* @vtlvariable name="data" type="org.apache.turbine.util.RunData" *#
 #* @vtlvariable name="turbineUtils" type="org.nrg.xdat.turbine.utils.TurbineUtils" *#
 #* @vtlvariable name="content" type="org.apache.turbine.services.pull.tools.ContentTool" *#
-#set($showUserList = !$turbineUtils.toBoolean($siteConfig.getProperty(
-    "restrictUserListAccessToAdmins", "true")) || $turbineUtils.isSiteAdmin($user))
+#set($showUserList = !$siteConfig.restrictUserListAccessToAdmins || $turbineUtils.isSiteAdmin($user))
 <script type="text/javascript" src="$content.getURI("scripts/project/userMgmt.js")"></script>
 
 <table class="mgmt_container">
diff --git a/src/main/webapp/xnat-templates/screens/xnat_subjectAssessorData/edit/subject.vm b/src/main/webapp/xnat-templates/screens/xnat_subjectAssessorData/edit/subject.vm
index d18841c695721b7419cccab9a6b670d389abd5ac..75425472f89d951a29bb1fc8b554a38702c46aab 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_subjectAssessorData/edit/subject.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_subjectAssessorData/edit/subject.vm
@@ -1,3 +1,4 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <table>
  <tr>
   <td>
@@ -20,7 +21,7 @@
     if(window.subjectEditor==undefined){
       var config=new Object();
 	  
-	  #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-subject-create-from-expt-edit","true")))
+	  #if($siteConfig.uiAllowSubjectCreateFromExptEdit)
 	  	config.create_subject_link=create_subject_link;
 	  #end
 
diff --git a/src/main/webapp/xnat-templates/screens/xnat_subjectData/actionsBox/ManageFiles.vm b/src/main/webapp/xnat-templates/screens/xnat_subjectData/actionsBox/ManageFiles.vm
index c3b4e3314981dec0167bd32f42e602a781c86b4d..232b1aefbf223e095ff638a85bc1e3e6969be164 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_subjectData/actionsBox/ManageFiles.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_subjectData/actionsBox/ManageFiles.vm
@@ -1,5 +1,6 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 <!-- Sequence: 5 -->
-#if($user.checkFeature($om,"manage_files") && $turbineUtils.toBoolean($siteConfig.getProperty("UI.show-manage-files","true")))
+#if($user.checkFeature($om,"manage_files") && $siteConfig.uiShowManageFiles)
      <li class="yuimenuitem">
 			<A href="" ONCLICK="showFiles();return false;"><div class="ic">#if($actionObject.hasImage())<img border="0" src="$content.getURI("scripts/yui/build/treeview/assets/img/folders/cf.gif")"/>#else&nbsp;#end</div><div class="ic_spacer">&nbsp;</div>Manage Files</A>
 	 </li>
diff --git a/src/main/webapp/xnat-templates/screens/xnat_subjectData/xnat_subjectData_assessor.vm b/src/main/webapp/xnat-templates/screens/xnat_subjectData/xnat_subjectData_assessor.vm
index 667c20fcf99684dc968718a56cccf449615fb1e5..749bb0dadb46223f19aedef7fb2d824199070473 100644
--- a/src/main/webapp/xnat-templates/screens/xnat_subjectData/xnat_subjectData_assessor.vm
+++ b/src/main/webapp/xnat-templates/screens/xnat_subjectData/xnat_subjectData_assessor.vm
@@ -1,9 +1,10 @@
+#* @vtlvariable name="siteConfig" type="org.nrg.xdat.preferences.SiteConfigPreferences" *#
 #if($assessor.canRead($user)==false)
 ##handles data that is not viewable by this user.  On some servers, users can see that these exist (but not view the actual data)
 ##on other servers, they shouldn't see that it exists at all.
 ##By default, the user shouldn't see these.
-##To allow, site administrators should configure the UI.allow-blocked-subject-assessor-view peroprty in siteConfiguration.properties
-    #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-blocked-subject-assessor-view","false")))
+##To allow, site administrators should configure the uiLoginFailureMessage peroprty in siteConfiguration.properties
+    #if($siteConfig.uiLoginFailureMessage)
         #set($denied=true)
     <TR BGCOLOR="FF9999">
         <TD>$!assessor.getProperty("date")</TD>