diff --git a/src/main/webapp/page/admin/data/config/site-admin-sample-new.yaml b/src/main/webapp/page/admin/data/config/site-admin-sample-new.yaml
index d2e3f3bb73f499e887419aeb23c1bdcc0c0b3923..f9ef0c4608f0f9b4bb0a02232db8d2ec8273fb69 100644
--- a/src/main/webapp/page/admin/data/config/site-admin-sample-new.yaml
+++ b/src/main/webapp/page/admin/data/config/site-admin-sample-new.yaml
@@ -2,15 +2,16 @@ root:
     kind: tabs
     name: siteAdmin
     label: Administer XNAT
-    groups:
-#        dashboard: Dashboard
-        xnatSetup: XNAT Setup
-        manageAccess: Manage Access
-        manageData: Manage Data
-        processing: Processing
-        projectCustomization: Project Customization
-        advanced: Advanced XNAT Settings
-        other: Other
+    meta:
+        tabGroups:
+            dashboard: Dashboard
+            xnatSetup: XNAT Setup
+            manageAccess: Manage Access
+            manageData: Manage Data
+            processing: Processing
+            projectCustomization: Project Customization
+            advanced: Advanced XNAT Settings
+            other: Other
     contains: tabs
     tabs:  # this property name is the same as 'contains', so it will be treated like 'contents'
         siteSetup:
@@ -49,12 +50,27 @@ root:
                                 html: Hello people.
                                 style:
                                     fontWeight: bold
+                manageThemes:
+                    kind: panel
+                    name: manageThemes
+                    label: Manage Themes
+                    footer: false
+                    contents:
+                        themeUpload:
+                            kind: panel.input.upload
+                            name: themeUpload
+                            # id: uplode-here
+                            method: PUT
+                            action: '/put/the/thing/in/the/stuff'
+                            label: Upload Theme File
+
+
 
         security:
             kind: tab
             name: security
             label: Security
-#            group: xnatSetup
+            group: xnatSetup
             contents:
                 generalSecuritySettings:
                     kind: panel.form
@@ -66,10 +82,9 @@ root:
                             name: securityChannel
                             label: Security Channel
                             value: https
-#                            contains: options
                             options:
                                 http:
-                                    label: http
+                                    # if there's no 'label' property, the value will be used for the label
                                     value: http
                                 https:
                                     label: https
diff --git a/src/main/webapp/scripts/lib/spawn/spawn.js b/src/main/webapp/scripts/lib/spawn/spawn.js
index c0d0f7ff37baa7933d66a1357334faca2e19009c..d4d43c6d982de7d3e93dd565aceb271e552e8f83 100644
--- a/src/main/webapp/scripts/lib/spawn/spawn.js
+++ b/src/main/webapp/scripts/lib/spawn/spawn.js
@@ -53,7 +53,8 @@
         'url',
         'checkbox',
         'radio',
-        'hidden'
+        'hidden',
+        'file'
     ];
 
     // use these as a shortcut to create <input> elements:
@@ -132,7 +133,7 @@
 
         if (tag === '!'){
             el = doc.createDocumentFragment();
-            appendChildren(el, opts, spawn);
+            appendChildren(el, opts||'', spawn);
             return el;
         }
 
diff --git a/src/main/webapp/scripts/xnat/ui/input.js b/src/main/webapp/scripts/xnat/ui/input.js
index 2d5bc1e28985f1a8e3fd91ccde146e6380504937..8e3451e9561ffa96a9bd0ef4a685d1b779f23818 100644
--- a/src/main/webapp/scripts/xnat/ui/input.js
+++ b/src/main/webapp/scripts/xnat/ui/input.js
@@ -82,7 +82,7 @@ var XNAT = getObject(XNAT);
 
     otherTypes = [
         'password', 'date', 'checkbox',
-        'radio', 'button', 'hidden'
+        'radio', 'button', 'hidden', 'file'
     ];
     otherTypes.forEach(function(type){
         input[type] = function(config){
diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js
index 04e84294301d0be5ea0c1fb95780abc914f13340..61dbf26d8983e755380af9c9f55eaa026809fd9f 100644
--- a/src/main/webapp/scripts/xnat/ui/panel.js
+++ b/src/main/webapp/scripts/xnat/ui/panel.js
@@ -16,13 +16,13 @@ var XNAT = getObject(XNAT || {});
 
     XNAT.ui.panel = panel =
         getObject(XNAT.ui.panel || {});
-    
+
     // add new element class without destroying existing class
     function addClassName(el, newClass){
         el.className = [].concat(el.className||[], newClass).join(' ').trim();
         return el.className;
     }
-    
+
     // add new data object item to be used for [data-] attribute(s)
     function addDataObjects(obj, attrs){
         obj.data = obj.data || {};
@@ -32,15 +32,32 @@ var XNAT = getObject(XNAT || {});
         return obj.data;
     }
 
-    panel.init = function(config){
-        var _target = spawn('div.panel-body'),
+    panel.init = function(opts){
+        
+        opts = getObject(opts);
+        opts.element = opts.element || opts.config || {};
+
+        var _target = spawn('div.panel-body', opts.element),
+
+            hideFooter = (isDefined(opts.footer) && (opts.footer === false || /^-/.test(opts.footer))),
+
             _panel  = spawn('div.panel.panel-default', [
                 ['div.panel-heading', [
-                    ['h3.panel-title', config.title || config.label]
+                    ['h3.panel-title', opts.title || opts.label]
                 ]],
+
+                // target is where the next spawned item will render
                 _target,
-                ['div.panel-footer', config.footer]
+
+                (hideFooter ? ['div.hidden'] : ['div.panel-footer', opts.footer])
+
             ]);
+        
+        // add an id to the outer panel element if present
+        if (opts.id || opts.element.id) {
+            _panel.id = (opts.id || opts.element.id) + '-panel';
+        }
+
         return {
             target: _target,
             element: _panel,
@@ -51,8 +68,16 @@ var XNAT = getObject(XNAT || {});
         }
     };
 
-    panel.form = function(config){
-        var _target = spawn('div.panel-body', config.config),
+    // creates a panel that's a form that can be submitted
+    panel.form = function(opts){
+
+        opts = getObject(opts);
+        opts.element = opts.element || opts.config || {};
+
+        var _target = spawn('div.panel-body', opts.element),
+
+            hideFooter = (isDefined(opts.footer) && (opts.footer === false || /^-/.test(opts.footer))),
+
             _footer = [
                 ['button.btn.btn-sm.btn-primary.save.pull-right|type=submit', 'Submit'],
                 ['span.pull-right', '&nbsp;&nbsp;&nbsp;'],
@@ -60,15 +85,27 @@ var XNAT = getObject(XNAT || {});
                 ['button.btn.btn-sm.btn-link.defaults.pull-left', 'Default Settings'],
                 ['div.clear']
             ],
-            _panel = spawn('form.xnat-form-panel.panel.panel-default', [
+
+            _panel = spawn('form.xnat-form-panel.panel.panel-default', {
+                method: opts.method || 'POST',
+                action: opts.action || '#'
+            }, [
                 ['div.panel-heading', [
-                    ['h3.panel-title', config.title || config.label]
+                    ['h3.panel-title', opts.title || opts.label]
                 ]],
-                
+
+                // target is where the next spawned item will render
                 _target,
-                
-                ['div.panel-footer', config.footer || _footer]
+
+                (hideFooter ? ['div.hidden'] : ['div.panel-footer', opts.footer || _footer])
+
             ]);
+
+        // add an id to the outer panel element if present
+        if (opts.id || opts.element.id) {
+            _panel.id = (opts.id || opts.element.id) + '-panel';
+        }
+
         return {
             target: _target,
             element: _panel,
@@ -78,31 +115,34 @@ var XNAT = getObject(XNAT || {});
             }
         }
     };
-    
+
     // create a single generic panel element
     panel.element = function(opts){
         var _element, _inner = [], _target;
         opts = getObject(opts);
-        opts.config = opts.config || opts.element || {};
-        addClassName(opts.config, 'panel-element');
-        addDataObjects(opts.config, { name: opts.name||'' });
+        opts.element = opts.element || opts.config || {};
+        if (opts.id || opts.element.id) {
+            opts.element.id = (opts.id || opts.element.id) + '-element';
+        }
+        addClassName(opts.element, 'panel-element');
+        addDataObjects(opts.element, { name: opts.name||'' });
         opts.label = opts.label||opts.title||opts.name||'';
 
         _inner.push(['label.element-label', opts.label]);
-        
+
         // 'contents' will be inserted into the 'target' element
-        _target = spawn(['div.element-wrapper']);
-        
+        _target = spawn('div.element-wrapper');
+
         // add the target to the content array
         _inner.push(_target);
-        
+
         // add a description if there is one
         if (opts.description){
             _inner.push(['div.description', opts.description||opts.body||opts.html]);
         }
-        
-        _element = spawn('div', opts.config, _inner);
-        
+
+        _element = spawn('div', opts.element, _inner);
+
         return {
             target: _target,
             element: _element,
@@ -111,7 +151,7 @@ var XNAT = getObject(XNAT || {});
                 return _element
             }
         }
-        
+
     };
 
     panel.input = {};
@@ -119,7 +159,7 @@ var XNAT = getObject(XNAT || {});
     panel.input.text = function(opts){
         return XNAT.ui.template.panelInput(opts).spawned;
     };
-    
+
     panel.input.number = function(opts){
         opts = getObject(opts);
         opts.type = 'number';
@@ -146,9 +186,37 @@ var XNAT = getObject(XNAT || {});
         //addClassName(opts, 'checkbox');
         return XNAT.ui.template.panelInput(opts).spawned;
     };
-    
+
+    panel.input.upload = function(opts){
+        opts = getObject(opts);
+        opts.id = (opts.id||randomID('upload-', false));
+        opts.element = opts.element || opts.config || {};
+        opts.element.id = opts.id;
+        var form = ['form', {
+            id: opts.id + '-form',
+            method: opts.method || 'POST',
+            action: opts.action || '#',
+            className: addClassName(opts, 'file-upload')
+        }, [
+            ['input', {
+                type: 'file',
+                id: opts.id + '-input',
+                multiple: true,
+                style: {
+                    width: '270px'
+                }
+            }],
+            ['button', {
+                type: 'button',
+                id: opts.id +'-button',
+                html: 'Upload'
+            }]
+        ]];
+        return XNAT.ui.template.panelInput(opts, form).spawned;
+    };
+
     panel.input.group = function(obj){
-        var _inner = spawn('div.element-group');
+        var _inner = ['div.element-group'];
         var _outer = XNAT.ui.template.panelElementGroup(obj, _inner);
         return {
             target: _inner,
diff --git a/src/main/webapp/scripts/xnat/ui/tabs.js b/src/main/webapp/scripts/xnat/ui/tabs.js
index 2fe2bdf9e3691cacd29daf313fb0d0b76fc250a6..ccebcc972b922dcf996cfbe3997f128ffc11ad9a 100755
--- a/src/main/webapp/scripts/xnat/ui/tabs.js
+++ b/src/main/webapp/scripts/xnat/ui/tabs.js
@@ -122,11 +122,23 @@ var XNAT = getObject(XNAT || {});
         }, [
             ['a', {
                 title: obj.label,
-                //href: '#'+obj.config.id,
+                // href: '#'+obj.config.id,
                 href: '#!',
                 html: obj.label
             }]
         ]);
+
+        // setup the footer for the whole tab pane
+        function paneFooter(){
+            return spawn('footer.footer', [
+                ['button', {
+                    type: 'button',
+                    html: 'Save All',
+                    classes: 'save-all btn btn-primary pull-right'
+                }]
+            ]);
+        }
+
         _pane = spawn('div.tab-pane', obj.config);
 
         if (obj.active) {
@@ -140,6 +152,7 @@ var XNAT = getObject(XNAT || {});
 
         function render(element){
             $$(element).append(_pane);
+            $$(element).append(paneFooter());
             return _pane;
         }
 
@@ -199,17 +212,6 @@ var XNAT = getObject(XNAT || {});
         //     return frag;
         // }
         //
-        // // setup the footer for the whole tab pane
-        // function paneFooter(){
-        //     var footer = spawn('footer.footer', [
-        //         ['button', {
-        //             type: 'button',
-        //             html: 'Save All',
-        //             classes: 'save-all btn btn-primary pull-right'
-        //         }]
-        //     ]);
-        //     return footer;
-        // }
         //
         // return frag;