diff --git a/src/main/java/org/nrg/xnat/turbine/modules/screens/Page.java b/src/main/java/org/nrg/xnat/turbine/modules/screens/Page.java
new file mode 100644
index 0000000000000000000000000000000000000000..e61e54d43eb5c0bde55c2cf0364fb3e1fe4ede6f
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/turbine/modules/screens/Page.java
@@ -0,0 +1,16 @@
+package org.nrg.xnat.turbine.modules.screens;
+
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+import org.nrg.xdat.security.helpers.UserHelper;
+import org.nrg.xdat.turbine.modules.screens.SecureScreen;
+import org.nrg.xdat.turbine.utils.TurbineUtils;
+import org.nrg.xft.security.UserI;
+
+
+public class Page extends SecureScreen {
+    @Override
+    protected void doBuildTemplate(RunData data, Context context) throws Exception {
+        //super.doBuildTemplate(data, context);
+    }
+}
diff --git a/src/main/webapp/WEB-INF/conf/xnat-security.xml b/src/main/webapp/WEB-INF/conf/xnat-security.xml
index 30dabc7727ba6be97d120e9f153787eea90fd9c8..bd9d468303fd964233f4e9fd4b1a4d8f659eb383 100644
--- a/src/main/webapp/WEB-INF/conf/xnat-security.xml
+++ b/src/main/webapp/WEB-INF/conf/xnat-security.xml
@@ -123,6 +123,9 @@
                 <value>/scripts/**</value>
                 <value>/style/**</value>
                 <value>/themes/**</value>
+                <value>/files/**</value>
+                <value>/pages/**</value>
+                <value>/page/**</value>
                 <value>/applet/**</value>
             </list>
         </property>
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 527141d8213104fa7dca48cbad3aea4cd9df343b..088099837bda4fb068894ee541f52bc31c0b2f26 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -15,6 +15,9 @@
     <!--                                                                          -->
     <!-- ======================================================================== -->
     <welcome-file-list>
+        <welcome-file>default.html</welcome-file>
+        <welcome-file>default.jsp</welcome-file>
+        <welcome-file>index.html</welcome-file>
         <welcome-file>index.jsp</welcome-file>
         <welcome-file>app</welcome-file>
     </welcome-file-list>
diff --git a/src/main/webapp/pages/foo.jsp b/src/main/webapp/pages/foo.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..b88053549c9c05f3fc76783e524519bb46ba8a75
--- /dev/null
+++ b/src/main/webapp/pages/foo.jsp
@@ -0,0 +1,3 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+<h1>Foo</h1>
diff --git a/src/main/webapp/scripts/globals.js b/src/main/webapp/scripts/globals.js
index 1198212052c90d04ada5101ad3c957d629c4a63b..a242fd93367b5443f149dacb85eb526dd3ae25c6 100644
--- a/src/main/webapp/scripts/globals.js
+++ b/src/main/webapp/scripts/globals.js
@@ -48,6 +48,22 @@ function getParameterByName( name ){
     return getQueryStringValue(name)
 }
 
+// get the url hash string without the '#'
+function getUrlHashString(){
+    return window.location.hash.split('#')[1] || '';
+}
+
+// simplest function for getting
+// a value from the url hash
+function getUrlHashValue(start, end){
+    var part = '',
+        hash = window.location.hash;
+    if (!hash) { return '' }
+    part = hash.split(start||'#')[1]||'';
+    part = part.split(end||'/')[0]||'';
+    return part;
+}
+
 function firstDefined() {
     var undefined, i = -1;
     while (++i < arguments.length) {
@@ -257,6 +273,39 @@ function cloneObject(obj){
     return extend(true, {}, obj);
 }
 
+// add child objects to 'obj' object from string
+// OVERWRITES PROPERTIES WITH MATCHING NAMES
+// setObject(foo, 'bar.baz', 123456)
+// -> foo: { bar: { baz: 123456 } }
+function setObject(obj, str, val) {
+    var parts, part;
+    if (typeof str != 'string' || !str.length) {
+        return {};
+    }
+    obj = getObject(obj);
+    parts = str.split('.');
+    while (parts.length > 1) {
+        part = parts.shift();
+        obj = getObject(obj);
+        if (!obj[part]) {
+            obj[part] = {};
+        }
+        obj = obj[part];
+    }
+    obj[parts[0]] = val || {};
+    return obj;
+}
+
+// add child objects to 'obj' object
+// OVERWRITES PROPERTIES WITH MATCHING NAMES
+function setExtendedObject(obj, str, val){
+    var newObj = {};
+    setObject(newObj, str, val);
+    newObj = extend(true, {}, obj, newObj);
+    return newObj;
+}
+
+
 // return the last item in an array-like object
 function getLast(arr){
     if (!arr) { return null }
diff --git a/src/main/webapp/scripts/xnat/app/customPage.js b/src/main/webapp/scripts/xnat/app/customPage.js
new file mode 100644
index 0000000000000000000000000000000000000000..a43f9578b928172c7a5aa9b259a4a7790ff366fd
--- /dev/null
+++ b/src/main/webapp/scripts/xnat/app/customPage.js
@@ -0,0 +1,92 @@
+/*!
+ * Retrieve custom pages via AJAX
+ * Used in: /xnat-templates/screens/Page.vm
+ */
+(function(NS, factory){
+    if (typeof define === 'function' && define.amd) {
+        define(factory);
+    }
+    else if (typeof exports === 'object') {
+        module.exports = factory();
+    }
+    else {
+        return factory(NS);
+    }
+}('app.customPage', function(NS, undefined){
+
+    // setExtendedObject() hasn't been tested yet
+    //var customPage = setExtendedObject(XNAT, NS);
+
+    var customPage = getObject(XNAT.app.customPage||{});
+    
+    customPage.getName = function(){
+        var name = getQueryStringValue('view');
+        name = name || getUrlHashValue('#view=');
+        name = name || getUrlHashString();
+        return name;
+    };
+
+    customPage.name = customPage.getName();
+
+    customPage.getPage = function(name, container){
+
+        var pagePaths = [],
+            themePaths = [];
+
+        name = name || customPage.getName();
+
+        // don't even bother if there's no name
+        if (!name) return;
+
+        var container$ = customPage.container || $$(container);
+
+        function getPage(path){
+            return XNAT.xhr.get({
+                url: XNAT.url.rootUrl(path),
+                dataType: 'html',
+                success: function(content){
+                    container$.html(content);
+                }
+            })
+        }
+
+        var setPaths = function(pg, prefixes){
+            var paths = [];
+            [].concat(prefixes).forEach(function(prefix){
+                paths.push(prefix + '/' + pg + '.jsp');
+                paths.push(prefix + '/' + pg + '.html');
+            });
+            return paths;
+        };
+
+        pagePaths = setPaths(name, ['/pages', '/page']);
+
+        // if we're using a theme, check that theme's folder
+        if (XNAT.theme){
+            themePaths = setPaths(name, [
+                '/themes/' + XNAT.theme,
+                '/themes/' + XNAT.theme + '/pages',
+                '/themes/' + XNAT.theme + '/page'
+            ]);
+            pagePaths = themePaths.concat(pagePaths);
+        }
+
+        function lookForPage(i) {
+            if (i === pagePaths.length){
+                console.log("couldn't do it");
+                return false;
+            }
+            // recursively try to get pages at different places
+            getPage(pagePaths[i]).fail(function(){
+                lookForPage(++i)
+            });
+        }
+
+        // do the stuff
+        lookForPage(0);
+
+    };
+
+    return customPage;
+
+}));
diff --git a/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm b/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
index b370bdf5ba00ecd776143e5b91fdaf8646af31be..aba1fe2c3cc61ce0165e34aebc738cd8506cf55f 100755
--- a/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
+++ b/src/main/webapp/xnat-templates/navigations/HeaderIncludes.vm
@@ -24,7 +24,7 @@
 
 #if ($theme)
 <script type="text/javascript">
-    XNAT.theme = $theme}";
+    XNAT.theme = "$theme";
 </script>
 #end
 
diff --git a/src/main/webapp/xnat-templates/screens/Page.vm b/src/main/webapp/xnat-templates/screens/Page.vm
new file mode 100644
index 0000000000000000000000000000000000000000..18c7aaa7e9cb85b71e8c385677c9447e5d644287
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/Page.vm
@@ -0,0 +1,51 @@
+#* @vtlvariable name="displayManager" type="org.nrg.xdat.display.DisplayManager" *#
+#* @vtlvariable name="par_count" type="java.lang.Integer" *#
+#* @vtlvariable name="data" type="org.apache.turbine.util.RunData" *#
+#* @vtlvariable name="turbineUtils" type="org.nrg.xdat.turbine.utils.TurbineUtils" *#
+#* @vtlvariable name="siteConfig" type="java.util.Properties" *#
+#* @vtlvariable name="content" type="org.apache.turbine.services.pull.tools.ContentTool" *#
+
+<!-- start xnat-templates/screens/Page.vm -->
+
+#set ($template = $data.getTemplateInfo())
+#set ($pg = $data.getParameters().getString('view'))
+#set ($pg_path = "/screens/pages/${pg}.vm")
+
+<script src="$content.getURI('/scripts/xnat/app/customPage.js')"></script>
+
+#if ($pg)
+
+    ## if there's a page at '*templates/pages/name.vm' then use that
+    #if ($turbineUtils.resourceExists($pg_path))
+        <div id="view-page">
+            #parse($pg_path)
+        </div>
+    #else
+
+    ## try to retrieve page content using the query string value
+    <div id="view-page"></div>
+    <script>
+        XNAT.app.customPage.getPage('${pg}', '#view-page');
+    </script>
+
+    #end
+
+#else
+
+    ## do stuff if there's no "view" query string param
+    <div id="view-page"></div>
+
+<script>
+
+    XNAT.app.customPage.container = $('#view-page');
+    XNAT.app.customPage.getPage();
+
+    window.onhashchange = function(){
+        XNAT.app.customPage.getPage();
+    }
+
+</script>
+
+#end
+
+<!-- end xnat-templates/screens/Page.vm -->
diff --git a/src/main/webapp/xnat-templates/screens/pages/hello.vm b/src/main/webapp/xnat-templates/screens/pages/hello.vm
new file mode 100644
index 0000000000000000000000000000000000000000..be45d32347447fdfdc9f7cfd3d87655fc30d5afa
--- /dev/null
+++ b/src/main/webapp/xnat-templates/screens/pages/hello.vm
@@ -0,0 +1 @@
+<h1>Hello.</h1>
\ No newline at end of file