From ac92a83bc170805bd02f703a9645e53652ed82ea Mon Sep 17 00:00:00 2001
From: "Mark M. Florida" <markflorida@wustl.edu>
Date: Mon, 17 Oct 2016 22:18:47 -0500
Subject: [PATCH] XNAT-4533, more: Added Plugin Settings page - 'Administer >
 Plugin Settings' menu option will show when there's an installed plugin that
 has site-wide settings. The mechanism for this is clunky and could be
 improved if there was a way for plugins to register themselves as having
 site-wide settings. Split up items in 'Administer' menu for more granularity
 in inserting menu items. Other refactoring to support these features,
 including a new panel.form contentType option 'text/json' that submits JSON
 as text (needed for XSync plugin). (WHY!?)

---
 .../webapp/page/admin/plugins/content.jsp     | 103 ++++++++++++++++++
 src/main/webapp/page/admin/plugins/index.jsp  |  20 ++++
 src/main/webapp/page/content.jsp              |  12 +-
 src/main/webapp/scripts/globals.js            |  10 +-
 .../scripts/xnat/admin/pluginSettings.js      |  89 +++++++++++++++
 src/main/webapp/scripts/xnat/spawner.js       |   2 -
 src/main/webapp/scripts/xnat/ui/panel.js      |   7 +-
 src/main/webapp/scripts/xnat/ui/select.js     |  41 ++++---
 src/main/webapp/scripts/xnat/ui/tabs.js       |  19 ++--
 src/main/webapp/scripts/xnat/xhr.js           |  10 +-
 .../screens/topBar/Administer/Automation.vm   |   3 +
 .../screens/topBar/Administer/DataTypes.vm    |   3 +
 .../screens/topBar/Administer/Default.vm      |  90 +++++++++++++--
 .../screens/topBar/Administer/Email.vm        |   3 +
 .../screens/topBar/Administer/Groups.vm       |   3 +
 .../screens/topBar/Administer/More.vm         |   3 +
 .../screens/topBar/Administer/Pipelines.vm    |   3 +
 .../topBar/Administer/PluginSettings.vm       |   5 +
 .../screens/topBar/Administer/SiteAdmin.vm    |   3 +
 .../screens/topBar/Administer/Users.vm        |   3 +
 20 files changed, 389 insertions(+), 43 deletions(-)
 create mode 100755 src/main/webapp/page/admin/plugins/content.jsp
 create mode 100644 src/main/webapp/page/admin/plugins/index.jsp
 create mode 100644 src/main/webapp/scripts/xnat/admin/pluginSettings.js
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/Automation.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/DataTypes.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/Email.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/Groups.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/More.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/Pipelines.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/PluginSettings.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/SiteAdmin.vm
 create mode 100644 src/main/webapp/xnat-templates/screens/topBar/Administer/Users.vm

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 00000000..c9e1fac5
--- /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 00000000..4ed945e8
--- /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 9994de23..d60e848c 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/scripts/globals.js b/src/main/webapp/scripts/globals.js
index 4301f3c2..f5748d4c 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/xnat/admin/pluginSettings.js b/src/main/webapp/scripts/xnat/admin/pluginSettings.js
new file mode 100644
index 00000000..fe547b20
--- /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/spawner.js b/src/main/webapp/scripts/xnat/spawner.js
index 5bb37654..e8c661fe 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 b72e956e..07fa28f9 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 ef8ef9d8..4da84d36 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,24 @@ 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' });
         
         if (config.options){
             if (Array.isArray(config.options)) {
                 forEach(config.options, function(opt){
-                    addOption(menu, opt);
+                    addOption(menu, opt, config.value);
                 })    
             }
             else {
@@ -99,14 +113,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/tabs.js b/src/main/webapp/scripts/xnat/ui/tabs.js
index 2305742c..9542a5cc 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 73421959..5f3754f4 100755
--- a/src/main/webapp/scripts/xnat/xhr.js
+++ b/src/main/webapp/scripts/xnat/xhr.js
@@ -593,11 +593,19 @@ var XNAT = getObject(XNAT||{}),
         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();
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 00000000..1396e02f
--- /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 00000000..61664a8a
--- /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 68db2d8e..5757cb5d 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 00000000..10d03d59
--- /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 00000000..0886e2dd
--- /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 00000000..a7af1794
--- /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 00000000..2abb6acc
--- /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 00000000..61631d42
--- /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 00000000..d5b31cee
--- /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 00000000..ebfc04f4
--- /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>
-- 
GitLab