diff --git a/src/main/java/org/nrg/xnat/configuration/ThemeConfig.java b/src/main/java/org/nrg/xnat/configuration/ThemeConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0337e268af035f1c599b72d867c55398ae0d454
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/configuration/ThemeConfig.java
@@ -0,0 +1,71 @@
+/*
+ * org.nrg.xnat.turbine.modules.screens.ManageProtocol
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2013, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ *
+ * Author: Justin Cleveland <clevelandj@wustl.edu>
+ * Last modified 2/8/2016 11:29 AM
+ */
+
+package org.nrg.xnat.configuration;
+
+import java.util.ArrayList;
+
+/**
+ * Created by jcleve01 on 2/8/2016.
+ */
+public class ThemeConfig {
+    private String name = null;
+    private String path = null;
+    private boolean enabled = true;
+    private ArrayList roles = new ArrayList();
+
+    /**
+     * Dummy constructor to make Jackson mapper happy. Don't explictly use this.
+     */
+    public ThemeConfig() {
+    }
+    public ThemeConfig(String themeName) {
+        this.name = themeName;
+    }
+    public ThemeConfig(String themeName, String themePath, boolean enabled) {
+        this.name = themeName;
+        this.path = themePath;
+        this.enabled = enabled;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public ArrayList getRoles() {
+        return roles;
+    }
+
+    public void setRoles(ArrayList roles) {
+        this.roles = roles;
+    }
+}
diff --git a/src/main/java/org/nrg/xnat/restlet/extensions/ThemeRestlet.java b/src/main/java/org/nrg/xnat/restlet/extensions/ThemeRestlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d072e0c0e17b9e6e64335db6262ea84dd07119ac
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/restlet/extensions/ThemeRestlet.java
@@ -0,0 +1,344 @@
+/*
+ * org.nrg.xnat.turbine.modules.screens.ManageProtocol
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2013, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ *
+ * Author: Justin Cleveland <clevelandj@wustl.edu>
+ * Last modified 1/25/2016 1:52 PM
+ */
+
+package org.nrg.xnat.restlet.extensions;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.fileupload.*;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nrg.action.ClientException;
+import org.nrg.xdat.security.helpers.Roles;
+import org.nrg.xnat.configuration.ThemeConfig;
+import org.nrg.xnat.restlet.XnatRestlet;
+import org.nrg.xnat.restlet.resources.SecureResource;
+import org.nrg.xnat.restlet.util.FileWriterWrapperI;
+import org.restlet.Context;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.StringRepresentation;
+import org.restlet.resource.Variant;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Created by jcleve01 on 1/26/2016.
+ */
+@XnatRestlet({"/theme"})
+public class ThemeRestlet extends SecureResource {
+    private static final Log _log = LogFactory.getLog(ThemeRestlet.class);
+    private static String themesPath;
+    protected final ObjectMapper mapper = new ObjectMapper();
+    private static final int FILE_BUFFER_SIZE = 4096;
+    private File themeFile = null;
+
+    public ThemeRestlet(Context context, Request request, Response response) throws UnsupportedEncodingException {
+        super(context, request, response);
+        setModifiable(true);
+        getVariants().add(new Variant(MediaType.APPLICATION_JSON));
+        themesPath = this.getHttpSession().getServletContext().getRealPath(File.separator)+"themes";
+        themeFile = new File(themesPath + File.separator + "theme.json");
+        File checkThemesPath = new File(themesPath);
+        if (!checkThemesPath.exists()) {
+            checkThemesPath.mkdir();
+        }
+    }
+
+    @Override
+    public boolean allowDelete() {
+        return true;
+    }
+
+    /** // TODO: convert to swagger doc tags
+     * Administrator use only
+     * Deletes the entire theme package folder specified by the "theme" query parameter. If it happens to be the
+     * currently applied theme, the current theme is set to null and the default XNAT theme will be restored.
+     */
+    @Override
+    public void handleDelete() {
+        String theme = this.getQueryVariable("theme");
+        if(theme != null && !theme.isEmpty()) {
+            File f = new File(themesPath + File.separator + theme);
+            if (Roles.isSiteAdmin(user)) {
+                try {
+                    FileUtils.deleteDirectory(f);
+                    ThemeConfig tc = getTheme();
+                    String themeName = (tc!=null)?tc.getName():null;
+                    if(theme != null && theme.equals(themeName)){
+                        setTheme(null);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, "Theme directory deletion failed.");
+                }
+            } else {
+                getResponse().setStatus(Status.CLIENT_ERROR_UNAUTHORIZED, "Only site administrators can delete themes.");
+            }
+        }
+    }
+
+    @Override
+    public boolean allowPut() {
+        return true;
+    }
+
+    /** // TODO: convert to swagger doc tags
+     * Administrator use only
+     * Sets the currently selected global theme configuration package by updating the theme.json file under the
+     * application's theme folder and caches a copy of it in the app server's application context for reference by any
+     * other pages or servlets that may need to alter their styles and behaviors to fit the overriding theme.
+     */
+    @Override
+    public void handlePut() {
+        if (Roles.isSiteAdmin(user)) {
+            boolean enabled = true;
+            String name = this.getQueryVariable("theme"), path = themesPath + File.separator + name;
+            if("true".equals(this.getQueryVariable("disabled"))){
+                enabled = false;
+            }
+            setTheme(new ThemeConfig(name, themesPath, enabled));
+        } else {
+            getResponse().setStatus(Status.CLIENT_ERROR_UNAUTHORIZED, "Only site administrators can set the site theme.");
+        }
+    }
+
+    @Override
+    public boolean allowPost() {
+        return true;
+    }
+
+    /** // TODO: convert to swagger doc tags
+     * Administrator use only
+     * Accepts a multipart form with a zip file upload and extracts its contents in the theme system folder.
+     * The structure of the zipped package must have only directories at it's root
+     * If successful, the first (root) directory name (or theme name) unzipped is returned in the response.
+     * This will overwrite any other directories already existing with the same name without warning.
+     */
+    @Override
+    public void handlePost() { //Representation entity) { // Upload Theme package
+        Representation result = null;
+        if (Roles.isSiteAdmin(user)) {
+            try {
+                String firstDirName = "";
+                FileWriterWrapperI fw;
+                List<FileWriterWrapperI> fws = this.getFileWriters();
+                if (fws.size() == 0) {
+                    this.getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+                    getResponse().setEntity("No valid files were uploaded.", MediaType.TEXT_PLAIN);
+                    return;
+                }
+                if (fws.size() > 1) {
+                    this.getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+                    getResponse().setEntity("Theme importer is limited to one uploaded theme package per request.", MediaType.TEXT_PLAIN);
+                    return;
+                }
+                fw = fws.get(0);                  //TODO: inspect this fw object a little more for metadata
+                // TODO: validate zipped media type (fail otherwise)
+                final InputStream is = fw.getInputStream();
+                ZipInputStream zipIn = new ZipInputStream(is);
+                ZipEntry entry = zipIn.getNextEntry();
+                int dirCount = 0;
+                while (entry != null) {  // iterate over entries in the zip file
+                    String filePath = themesPath + File.separator + entry.getName();
+                    if (!entry.isDirectory()) {  // if the entry is a file, extract it      // TODO: Make sure we get a directory the first iteration through (fail otherwise) so that no files get dumped in the root themes directory
+                        extractFile(zipIn, filePath);
+                    } else {  // if the entry is a directory, make the directory
+                        if(dirCount == 0) {
+                            firstDirName = entry.getName();
+                            int slashIndex = firstDirName.indexOf('/');
+                            if(slashIndex>1){
+                                firstDirName = firstDirName.substring(0, slashIndex);
+                            }
+                        }
+                        dirCount++;
+                        File dir = new File(filePath);
+                        dir.mkdir();
+                    }
+                    zipIn.closeEntry();
+                    entry = zipIn.getNextEntry();
+                }
+                zipIn.close();
+                is.close();
+                getResponse().setEntity(firstDirName, MediaType.TEXT_PLAIN);
+            } catch (FileUploadBase.InvalidContentTypeException icte){
+                this.getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+                getResponse().setEntity("Invalid Content Type: "+icte.getMessage(), MediaType.TEXT_PLAIN);
+            } catch (FileUploadException fue) {
+                this.getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+                getResponse().setEntity("File Upload Exception: " + fue.getMessage(), MediaType.TEXT_PLAIN);
+            } catch (ClientException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            getResponse().setStatus(Status.CLIENT_ERROR_UNAUTHORIZED, "Only site administrators can upload new themes.");
+        }
+    }
+
+    /**
+     * Extracts a zip entry (file entry)
+     * @param zip
+     * @param path
+     * @throws IOException
+     */
+    private void extractFile(ZipInputStream zip, String path) throws IOException {
+        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(path));
+        byte[] bytes = new byte[FILE_BUFFER_SIZE];
+        int length = 0;
+        while ((length = zip.read(bytes)) != -1) {
+            os.write(bytes, 0, length);
+        }
+        os.close();
+    }
+
+    /** // TODO: convert to swagger doc tags
+     * Reports the currently selected global theme (if there is one), whether or not it's enabled, and a list of
+     * available themes on the system in a JSON string.
+     */
+    @Override
+    public Representation represent(Variant variant){
+        String themeOptions = null, themeName = null;
+        boolean themeEnabled = false;
+        ThemeConfig theme = getTheme();
+        try {
+            themeOptions = mapper.writeValueAsString(loadExistingThemes().toArray());
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        }
+        if(theme != null) {
+            themeName="\""+theme.getName()+"\"";
+            themeEnabled=theme.isEnabled();
+        }
+        return new StringRepresentation(
+                "{\"theme\":"+themeName+", \"enabled\":"+themeEnabled+", \"themeOptions\":"+themeOptions+"}",
+                MediaType.APPLICATION_JSON);
+    }
+
+    /**
+     * Loads the system theme options
+     * @return The list of the available theme packages (folder names) available under the system themes directory
+     */
+    public ArrayList<TypeOption> loadExistingThemes() {
+        ArrayList<TypeOption> themeOptions = new ArrayList<>();
+        themeOptions.add(new TypeOption(null, "None"));
+        File f = new File(themesPath); // current directory
+        FileFilter directoryFilter = new FileFilter() {
+            public boolean accept(File file) {
+                return file.isDirectory();
+            }
+        };
+        File[] files = f.listFiles(directoryFilter);
+        if(files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    themeOptions.add(new TypeOption(file.getName(), file.getName()));
+                }
+            }
+        }
+        return themeOptions;
+    }
+
+    /**
+     * Gets the currently selected system theme from an application servlet context cache, or secondarily from the
+     * theme.json file in the themes folder.
+     * @return The currently selected system theme configuration
+     */
+    public ThemeConfig getTheme() {
+        ThemeConfig themeConfig = null, cachedTheme = (ThemeConfig) this.getHttpSession().getServletContext().getAttribute("theme");
+        if(cachedTheme != null){
+            return cachedTheme;
+        } else {                        // Read the last saved theme selection from the theme.json file in the themes
+            if (themeFile.exists()) {   // directory in the event it can't be found in the application context.
+                try {                   // (ie. the server was just started/restarted)
+                    BufferedReader reader = new BufferedReader(new FileReader(themeFile));
+                    StringBuilder sb = new StringBuilder();
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        sb.append(line).append("\n");
+                    }
+                    reader.close();
+                    String contents = sb.toString();
+                    themeConfig = mapper.readValue(contents, ThemeConfig.class);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            setTheme(themeConfig);
+        }
+        return themeConfig;
+    }
+
+    /**
+     * Sets the currently selected system theme in the theme.json file in the web application's themes folder and
+     * stores it in the application servlet context cache
+     * @param themeConfig the theme configuration object to apply
+     */
+    public void setTheme(ThemeConfig themeConfig) {
+        try {
+            if (themeConfig != null) {
+                String themeJson = mapper.writeValueAsString(themeConfig);
+                if (!themeFile.exists()) {
+                    themeFile.createNewFile();
+                }
+                FileWriter writer = new FileWriter(themeFile);
+                writer.write(themeJson);
+                writer.flush();
+                writer.close();
+            } else {
+                themeFile.delete();
+                if (themeFile.exists()) {                           // Backup hack in case the file deletion does not
+                    FileWriter writer = new FileWriter(themeFile);  // succeed due to a file lock which sometimes
+                    writer.write("{\"name\":null}");                // strangely happens in Windows
+                    writer.flush();                                 // ...in that case wipe it out.
+                    writer.close();
+                }
+            }
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+            // TODO: rethrow this and respond as an internal server error
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        this.getHttpSession().getServletContext().setAttribute("theme", themeConfig);
+    }
+
+    /**
+     * Helper class to organize the available themes for display in a select dropdown form
+     */
+    public static class TypeOption implements Comparable<TypeOption> {
+        String value, label;
+        private TypeOption(String value, String label) {
+            this.value = value;
+            this.label = label;
+        }
+        public String getValue() {
+            return value;
+        }
+        public String getLabel() {
+            return label;
+        }
+        @Override
+        public int compareTo(TypeOption that) {
+            return this.label.compareToIgnoreCase(that.label);
+        }
+    }
+}
diff --git a/src/main/java/org/nrg/xnat/turbine/modules/screens/XDATScreen_themes.java b/src/main/java/org/nrg/xnat/turbine/modules/screens/XDATScreen_themes.java
new file mode 100644
index 0000000000000000000000000000000000000000..54957d5e43cb9116eeb4a604a67e99f92696938b
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/turbine/modules/screens/XDATScreen_themes.java
@@ -0,0 +1,30 @@
+/*
+ * org.nrg.xnat.turbine.modules.screens.ManageProtocol
+ * XNAT http://www.xnat.org
+ * Copyright (c) 2013, Washington University School of Medicine
+ * All Rights Reserved
+ *
+ * Released under the Simplified BSD.
+ *
+ * Author: Justin Cleveland <clevelandj@wustl.edu>
+ * Last modified 1/22/2016 3:20 PM
+ */
+
+package org.nrg.xnat.turbine.modules.screens;
+
+import org.apache.log4j.Logger;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+import org.nrg.xdat.turbine.modules.screens.SecureScreen;
+import org.nrg.xnat.restlet.extensions.ThemeRestlet;
+
+
+/**
+ * Created by jcleve01 on 1/22/2016.
+ */
+public class XDATScreen_themes extends SecureScreen  {
+    public final static Logger logger = Logger.getLogger(XDATScreen_themes.class);
+    @Override
+    protected void doBuildTemplate(RunData data, Context context) throws Exception {
+    }
+}
diff --git a/src/main/webapp/WEB-INF/conf/InstanceSettings.xml b/src/main/webapp/WEB-INF/conf/InstanceSettings.xml
deleted file mode 100644
index f7fff52df3be155efcf77337fbd2df6e4f09540a..0000000000000000000000000000000000000000
--- a/src/main/webapp/WEB-INF/conf/InstanceSettings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- edited with XMLSPY v2004 rel. 3 U (http://www.xmlspy.com) by Tim Olsen (Washington University) -->
-<Instance_Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="schemas/xdat/instance.xsd"
-				   site_url="http://localhost:8080" admin_email="administrator@xnat.org" archive_root_path="/data/xnat/archive"
-				   prearchive_path="/data/xnat/prearchive" cache_path="/data/xnat/cache"
-				   smtp_server="irony.wusm.wustl.edu" ftp_path="/data/xnat/ftp" build_path="/data/xnat/build"
-				   pipeline_path="/data/xnat/pipeline" require_login="true" user_registration="false" enable_csrf_token="true">
-	<Databases>
-		<Database Type="POSTGRESQL" Id="xnat" Driver="org.postgresql.Driver" Url="jdbc:postgresql://localhost/xnat" User="xnat" Pass="xnat" MaxConnections="10"/>
-	</Databases>
-	<Models>
-		<Data_Model File_Name="security.xsd" File_Location="schemas/security" DB="xnat"/>
-		<Data_Model File_Name="birnprov.xsd" File_Location="schemas/birn" DB="xnat"/>
-		<Data_Model File_Name="xnat.xsd" File_Location="schemas/xnat" DB="xnat"/>
-		<Data_Model File_Name="workflow.xsd" File_Location="schemas/pipeline" DB="xnat"/>
-		<Data_Model File_Name="repository.xsd" File_Location="schemas/pipeline" DB="xnat"/>
-		<Data_Model File_Name="project.xsd" File_Location="schemas/project" DB="xnat"/>
-		<Data_Model File_Name="assessments.xsd" File_Location="schemas/assessments" DB="xnat"/>
-		<Data_Model File_Name="catalog.xsd" File_Location="schemas/catalog" DB="xnat"/>
-		<Data_Model File_Name="protocolValidation.xsd" File_Location="schemas/validation" DB="xnat"/>
-		<Data_Model File_Name="screeningAssessment.xsd" File_Location="schemas/screening" DB="xnat"/>
-
-<!--<Data_Model File_Name="ext.xsd" File_Location="schemas/ext" DB="xnat"/>-->
-		<!-- Start modules schemas: Reserved for xnat_builder, don't modify anything between this line and the end modules line. -->
-		<!-- End modules schemas: Reserved for xnat_builder, don't modify anything between this line and the start modules line. -->
-	</Models>
-</Instance_Settings>
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index 4eb56411ccd19b6c6f6b5900afe04c7c2073677f..0000000000000000000000000000000000000000
--- a/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,326 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ D:/Development/XNAT/1.6/xnat_builder_1_6dev/plugin-resources/originals/application-web.xml
-  ~ 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
-  -->
-<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
-  <!-- ======================================================================== -->
-  <!--                                                                          -->
-  <!-- Uncomment the following block if you want to use the Session Service     -->
-  <!--                                                                          -->
-  <!-- ======================================================================== -->
-  <!--
-  	<listener>
-    	<listener-class>org.apache.turbine.services.session.SessionListener</listener-class>
-  	</listener>
-	-->
-  <!-- HttpSessionEventPublisher enables session counting for the concurrent session filter, XnatSessionEventPublisher requires the contextAttribute parameter -->
-  <context-param>
-    <param-name>contextAttribute</param-name>
-    <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring-mvc</param-value>
-  </context-param>
-  <context-param>
-    <param-name>org.restlet.component</param-name>
-    <param-value>org.nrg.xnat.restlet.XNATComponent</param-value>
-  </context-param>
-  <!--
-    	 This setting will be automatically configured according to xdat.url in build.properties.
-    	 You can still set it manually if desired (not here - do it in xnat/projects/xnat/src/web-conf/web-projectMerge.xml).
-    -->
-  <context-param>
-    <param-name>org.restlet.autoWire</param-name>
-    <param-value>false</param-value>
-  </context-param>
-  <filter>
-    <filter-name>springSecurityFilterChain</filter-name>
-    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
-    <init-param>
-      <param-name>contextAttribute</param-name>
-      <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring-mvc</param-value>
-    </init-param>
-  </filter>
-  <filter>
-    <filter-name>updateExpirationCookie</filter-name>
-    <filter-class>org.nrg.xnat.restlet.util.UpdateExpirationCookie</filter-class>
-  </filter>
-  <filter-mapping>
-    <filter-name>springSecurityFilterChain</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
-  <filter-mapping>
-    <filter-name>updateExpirationCookie</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
-  <listener>
-    <listener-class>org.nrg.xnat.security.XnatSessionEventPublisher</listener-class>
-  </listener>
-  <listener>
-    <listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class>
-  </listener>
-  <servlet>
-    <servlet-name>xnat17</servlet-name>
-    <servlet-class>org.apache.turbine.Turbine</servlet-class>
-    <init-param>
-      <param-name>properties</param-name>
-      <param-value>WEB-INF/conf/TurbineResources.properties</param-value>
-    </init-param>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-  <!-- BEGIN XNAT CUSTOMIZATIONS -->
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Spring Framework -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>spring-mvc</servlet-name>
-    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-    <init-param>
-      <param-name>contextConfigLocation</param-name>
-      <param-value>/WEB-INF/conf/root-spring-config.xml</param-value>
-    </init-param>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-  <servlet>
-    <servlet-name>xdat</servlet-name>
-    <servlet-class>org.nrg.xdat.servlet.XDATServlet</servlet-class>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR RESTServlet -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>RestletServlet</servlet-name>
-    <servlet-class>org.nrg.xnat.restlet.servlet.XNATRestletServlet</servlet-class>
-    <load-on-startup>2</load-on-startup>
-  </servlet>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR ArchiveServlet -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>ArchiveServlet</servlet-name>
-    <servlet-class>org.nrg.xnat.servlet.ArchiveServlet</servlet-class>
-    <load-on-startup>2</load-on-startup>
-  </servlet>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Ajax -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>XDATAjaxServlet</servlet-name>
-    <servlet-class>org.nrg.xdat.servlet.XDATAjaxServlet</servlet-class>
-  </servlet>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Apache Axis -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>AxisServlet</servlet-name>
-    <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>AdminServlet</servlet-name>
-    <servlet-class>org.apache.axis.transport.http.AdminServlet</servlet-class>
-    <load-on-startup>100</load-on-startup>
-  </servlet>
-  <servlet>
-    <servlet-name>SOAPMonitorService</servlet-name>
-    <servlet-class>org.apache.axis.monitor.SOAPMonitorService</servlet-class>
-    <init-param>
-      <param-name>SOAPMonitorPort</param-name>
-      <param-value>5001</param-value>
-    </init-param>
-    <load-on-startup>100</load-on-startup>
-  </servlet>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Image Viewer -->
-  <!-- ======================================================================== -->
-  <servlet>
-    <servlet-name>PopulateServlet</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.PopulateServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>ImageDistributorServlet</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.ImageDistributorServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>ImageLoaderServlet</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.ImageLoaderServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>MontageImageLoaderServlet</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.MontageImageLoaderServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>GetRecFileContents</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.GetRecFileContents</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>getAsegRegionVolumes</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.getAsegRegionVolumes</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>PublisherServlet</servlet-name>
-    <servlet-class>org.nrg.plexiViewer.Servlet.PublisherServlet</servlet-class>
-  </servlet>
-  <servlet-mapping>
-    <servlet-name>spring-mvc</servlet-name>
-    <url-pattern>/admin/*</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>spring-mvc</servlet-name>
-    <url-pattern>/xapi/*</url-pattern>
-  </servlet-mapping>
-  <!-- END XNAT CUSTOMIZATIONS -->
-  <servlet-mapping>
-    <servlet-name>xnat17</servlet-name>
-    <url-pattern>/app/*</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>xdat</servlet-name>
-    <url-pattern>/xdat/*</url-pattern>
-  </servlet-mapping>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR RESTServlet  -->
-  <!-- ======================================================================== -->
-  <servlet-mapping>
-    <servlet-name>RestletServlet</servlet-name>
-    <url-pattern>/REST/*</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>RestletServlet</servlet-name>
-    <url-pattern>/data/*</url-pattern>
-  </servlet-mapping>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Ajax -->
-  <!-- ======================================================================== -->
-  <servlet-mapping>
-    <servlet-name>XDATAjaxServlet</servlet-name>
-    <url-pattern>/ajax/*</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>XDATAjaxServlet</servlet-name>
-    <url-pattern>/servlet/XDATAjaxServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>XDATAjaxServlet</servlet-name>
-    <url-pattern>/servlet/AjaxServlet</url-pattern>
-  </servlet-mapping>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Apache Axis -->
-  <!-- ======================================================================== -->
-  <servlet-mapping>
-    <servlet-name>AdminServlet</servlet-name>
-    <url-pattern>/servlet/AdminServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>AxisServlet</servlet-name>
-    <url-pattern>/servlet/AxisServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>AxisServlet</servlet-name>
-    <url-pattern>*.jws</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>AxisServlet</servlet-name>
-    <url-pattern>/services/*</url-pattern>
-  </servlet-mapping>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR Image Viewer -->
-  <!-- ======================================================================== -->
-  <servlet-mapping>
-    <servlet-name>SOAPMonitorService</servlet-name>
-    <url-pattern>/SOAPMonitor</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>PopulateServlet</servlet-name>
-    <url-pattern>/servlet/PopulateServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>ImageDistributorServlet</servlet-name>
-    <url-pattern>/servlet/ImageDistributorServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>ImageLoaderServlet</servlet-name>
-    <url-pattern>/servlet/ImageLoaderServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>MontageImageLoaderServlet</servlet-name>
-    <url-pattern>/servlet/MontageImageLoaderServlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>GetRecFileContents</servlet-name>
-    <url-pattern>/servlet/GetRecFileContents</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>getAsegRegionVolumes</servlet-name>
-    <url-pattern>/servlet/getAsegRegionVolumes</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>PublisherServlet</servlet-name>
-    <url-pattern>/servlet/PublisherServlet</url-pattern>
-  </servlet-mapping>
-  <!-- ======================================================================== -->
-  <!-- INSERTED FOR ArchiveServlet -->
-  <!-- ======================================================================== -->
-  <servlet-mapping>
-    <servlet-name>ArchiveServlet</servlet-name>
-    <url-pattern>/archive/*</url-pattern>
-  </servlet-mapping>
-  <session-config>
-    <!-- Default to 15 minute session timeouts -->
-    <session-timeout>15</session-timeout>
-  </session-config>
-  <mime-mapping>
-    <extension>wsdl</extension>
-    <mime-type>text/xml</mime-type>
-  </mime-mapping>
-  <mime-mapping>
-    <extension>xsd</extension>
-    <mime-type>text/xml</mime-type>
-  </mime-mapping>
-  <!-- ======================================================================== -->
-  <!--                                                                          -->
-  <!-- Redirect the home page of the application to the turbine servlet         -->
-  <!--                                                                          -->
-  <!-- ======================================================================== -->
-  <welcome-file-list>
-    <welcome-file>index.jsp</welcome-file>
-    <welcome-file>app</welcome-file>
-  </welcome-file-list>
-  <!-- ======================================================================== -->
-  <!--                                                                          -->
-  <!-- Mapping HTTP error codes and exceptions to custom error pages to make    -->
-  <!-- the display a bit more pleasant and preserve system confidentiality.     -->
-  <!--                                                                          -->
-  <!-- ======================================================================== -->
-  <error-page>
-    <exception-type>java.lang.Throwable</exception-type>
-    <location>/app/template/Error.vm</location>
-  </error-page>
-  <!-- ======================================================================== -->
-  <!--                                                                          -->
-  <!-- Make sure that templates, resources and logs are not available through   -->
-  <!-- the servlet container. Remove security constraints or add an authen-     -->
-  <!-- tication role if you need access to these paths.                         -->
-  <!--                                                                          -->
-  <!-- ======================================================================== -->
-  <security-constraint>
-    <web-resource-collection>
-      <web-resource-name>templates</web-resource-name>
-      <url-pattern>/templates/*</url-pattern>
-    </web-resource-collection>
-    <web-resource-collection>
-      <web-resource-name>logs</web-resource-name>
-      <url-pattern>/logs/*</url-pattern>
-    </web-resource-collection>
-    <web-resource-collection>
-      <web-resource-name>resources</web-resource-name>
-      <url-pattern>/resources/*</url-pattern>
-    </web-resource-collection>
-    <auth-constraint />
-  </security-constraint>
-</web-app>
-
diff --git a/src/main/webapp/scripts/footer.js b/src/main/webapp/scripts/footer.js
index 177a65065693896b8f25c68ae7f7634feed7e6af..98590d8ac31767d4ba254ddc8eeb2f76a5764c07 100644
--- a/src/main/webapp/scripts/footer.js
+++ b/src/main/webapp/scripts/footer.js
@@ -544,6 +544,23 @@ $(function(){
 });
 
 jq(window).load(function(){
+    // adding shortcut methods to put and delete AJAX calls for clarity
+    jq.each(["put", "delete"], function(i, method) {
+        jq[method] = function(url, data, callback, type) {
+            if (jq.isFunction(data)) {
+                type = type || callback;
+                callback = data;
+                data = undefined;
+            }
+            return jq.ajax({
+                url: url,
+                type: method,
+                dataType: type,
+                data: data,
+                success: callback
+            });
+        };
+    });
 
     // trying to make the text readable
     jq('[style*="font-size:8px"]').addClass('smallest_text');
diff --git a/src/main/webapp/xnat-templates/screens/XDATScreen_admin_options.vm b/src/main/webapp/xnat-templates/screens/XDATScreen_admin_options.vm
index a1a2af2ffbb51a55223c8bce529a4f75ee557a02..98c311be4ebdd84fec77db45264e344d9af4cd29 100644
--- a/src/main/webapp/xnat-templates/screens/XDATScreen_admin_options.vm
+++ b/src/main/webapp/xnat-templates/screens/XDATScreen_admin_options.vm
@@ -53,6 +53,8 @@ $!template.setLayoutTemplate("DefaultExptList.vm")
 
         <a href="$link.setPage("XDATScreen_add_xnat_projectData.vm")">Create $displayManager.getSingularDisplayNameForProject()</a>
 
+        <a href="$link.setPage("XDATScreen_themes.vm")">Manage Themes</a>
+
         #addGlobalCustomScreens("admin/options")
 
     </div>
diff --git a/src/main/webapp/xnat-templates/screens/XDATScreen_themes.vm b/src/main/webapp/xnat-templates/screens/XDATScreen_themes.vm
new file mode 100644
index 0000000000000000000000000000000000000000..f9d016eda9441f8c2ea6f97ac5cd1799f05038ca
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/XDATScreen_themes.vm
@@ -0,0 +1,112 @@
+<!-- Begin XDATScreen_themes.vm -->
+<style>
+    .bold{
+        font-weight: bold;
+    }
+</style>
+<div style="margin-left: 20px;">
+	<h2><span id="titleAppName"></span> Theme Management</h2>
+    <div id="currentThemeDiv">
+        <span class="label bold">Current theme: </span>
+        <span id="currentTheme">None</span>
+    </div>
+    <br>
+    <div id="selectThemeDiv">
+        <span class="label bold">Select an existing theme: </span>
+        <select id="themeSelection" name="theme" style="width: 270px;"></select>&nbsp;
+        <button id="submitThemeButton" onclick="setTheme();">Set Theme</button>&nbsp;
+        <button id="removeThemeButton" onclick="removeTheme();">Remove Theme</button>
+    </div>
+	<br>
+    <form id="uploadThemeForm" method="POST" class="optOutOfXnatDefaultFormValidation">
+        <span class="label bold">Upload a theme package: </span>
+        <input type="file" id="themeFileUpload" name="themeFileUpload" multiple style="width: 270px;"/>&nbsp;
+        <button type="submit" id="submitThemeUploadButton">Upload</button>
+    </form>
+</div>
+
+<script>
+    var themeUrl = "$content.getURI('/data/theme')";
+    var q = '?', a = '&';
+    var csrf = 'XNAT_CSRF='+window.csrfToken;
+	$('#titleAppName').text(XNAT.app.siteId);
+    var currentTheme = $('#currentTheme');
+    var themeSelector = $('#themeSelection');
+    var uploadForm = document.getElementById('uploadThemeForm');
+    var themeUploader = document.getElementById('themeFileUpload');
+    var themeUploadSubmit = document.getElementById('submitThemeUploadButton');
+    var selectedTheme = null;
+	function populateThemes(themeToSelect){
+        $.get(themeUrl+q+csrf, null, function (data){
+            themeSelector.empty();
+            $(data.themeOptions).each(function(i, opt) {
+                var selected = '', selectedTheme = null;
+                if(data.theme == opt.value){
+                    selectedTheme = data.theme;
+                    selected = ' selected="selected"';
+                    currentTheme.text(selectedTheme?selectedTheme:'None');
+                }
+                themeSelector.append('<option value="'+opt.value+'"'+selected+'>'+opt.label+'</option>');
+            });
+            if(themeToSelect && typeof themeToSelect === 'string'){
+                themeSelector.val(themeToSelect);
+            }
+        }, 'json');
+	}
+    function setTheme(){
+        xmodal.confirm({
+            content: 'Theme selection appearances may not fully take effect until users log out, clear their browser cache and log back in.'+
+                     '<br><br>Are you sure you wish to change the global theme?',
+            action: function(){
+                $.put(themeUrl+q+'theme='+encodeURI(themeSelector.val())+a+csrf, null, populateThemes);
+            }
+        });
+    }
+	function removeTheme(){
+		xmodal.confirm({
+			content: 'Are you sure you wish to delete the selected theme?',
+			action: function(){
+                $.delete(themeUrl+q+'theme='+encodeURI(themeSelector.val())+a+csrf, null, populateThemes);
+			}
+		});
+	}
+
+    /*** Theme Package Upload Functions ***/
+    uploadForm.action = themeUrl+q+csrf;
+    uploadForm.onsubmit = function(event) {
+        event.preventDefault();
+        $(themeUploadSubmit).text('Uploading...');
+        $(themeUploadSubmit).attr('disabled', 'disabled');
+        var files = themeUploader.files;
+        var formData = new FormData();
+        var uploaded = false;
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            if (!file.type.match('zip.*')) {
+                continue;
+            }
+            formData.append('themes[]', file, file.name);
+            var xhr = new XMLHttpRequest();
+            xhr.open('POST', uploadForm.action, true);
+            xhr.onload = function () {
+                if (xhr.status !== 200) {
+                    xmodal.message('Upload Error', xhr.responseText);
+                }
+                var uploadedTheme = xhr.responseText;
+                $(themeUploadSubmit).text('Upload');
+                $(themeUploadSubmit).removeAttr('disabled');
+                populateThemes(uploadedTheme);
+            };
+            xhr.send(formData);
+            uploaded = true;
+        }
+        if(!uploaded){
+            xmodal.message('Nothing Uploaded', 'No valid theme package files were selected for upload.');
+            $(themeUploadSubmit).text('Upload');
+            $(themeUploadSubmit).removeAttr('disabled');
+        }
+    }
+	$(populateThemes);  // ...called once DOM is fully loaded "ready"
+</script>
+
+<!-- End XDATScreen_themes.vm -->
\ No newline at end of file