From 4409232065d68929eac09a9c3584dae03c56eb03 Mon Sep 17 00:00:00 2001
From: "Mark M. Florida" <markflorida@wustl.edu>
Date: Wed, 31 Aug 2016 19:59:48 -0500
Subject: [PATCH] XNAT-4400: Changed checkboxes in Site Admin to new
 "switchbox" widget (panel.input.switchbox); stripped colon prefix from
 spawner elements' [data-name] attribute; other minor fixes and improvements
 to ui elements.

---
 .../xnat/spawner/site-admin-elements.yaml     | 46 +++++++--------
 src/main/webapp/scripts/xnat/ui/panel.js      | 31 ++++++++--
 src/main/webapp/scripts/xnat/ui/select.js     | 10 +++-
 src/main/webapp/scripts/xnat/ui/templates.js  | 25 +++++++--
 src/main/webapp/style/app.css                 | 56 +++++++++++++++++--
 5 files changed, 130 insertions(+), 38 deletions(-)

diff --git a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
index 463a79e0..ba054378 100644
--- a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
+++ b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
@@ -260,17 +260,17 @@ generalSecuritySettings:
             element:
                 title: Security Channel
         requireLogin:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: requireLogin
             label: Require User Login
             description: "If checked, then only registered users will be able to access your site. If false, anyone visiting your site will automatically be logged in as 'guest' with access to public data."
         restrictUserListAccessToAdmins:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: restrictUserListAccessToAdmins
             label: "Restrict user list access <br>to site administrators?"
             description: "Should this site restrict access to the list of system users to site administrators only? If turned on, the site is more secure, but this restricts project owners from being able to administer users in their projects directly."
         uiAllowNonAdminProjectCreation:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":UI.allow-non-admin-project-creation"
             label: "Allow non-administrators <br>to create projects?"
             description: "Should this site allow non-administrator users to create new projects? If turned on, the site is more secure, but this can make it more difficult for regular users to create new projects for their research efforts."
@@ -449,7 +449,7 @@ passwords:
               Interval for which users cannot reuse an old password of theirs. Uses
               <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>.
         requireSaltedPasswords:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             id: requireSaltedPasswords
             name: requireSaltedPasswords
             label: Require Passwords To Be Salted
@@ -463,12 +463,12 @@ csrf:
     contentType: json
     contents:
         enableCsrfToken:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: enableCsrfToken
             label: Require CSRF Token?
             description: Should this site require the use of a token to prevent CSRF attacks on POST, PUT, and DELETEs?
         csrfEmailAlert:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: csrfEmailAlert
             label: CSRF Email Alert
             description: "Should this site send an email to the site admin whenever a CSRF attack is attempted?"
@@ -511,7 +511,7 @@ emailServerSettings:
     label: Mail Server Settings
     contents:
         smtpEnabled:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":smtp.enabled"
             label: "Enable SMTP?"
         hostname:
@@ -544,11 +544,11 @@ emailServerSettings:
             name: mailServerProperties
             label: Properties
         smtpAuth:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":mail.smtp.auth"
             label: SMTP Auth?
         smtpStartTls:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":mail.smtp.starttls.enable"
             label: Start TLS?
         smtpSSLTrust:
@@ -649,7 +649,7 @@ notifications:
             description: "What email address(es) should receive update emails. Separate multiple email addresses with commas. If empty, emails will be sent to the site administrator email address."
             value: "!? XNAT.data.notifications['notifications.emailRecipientUpdate'] || XNAT.data.siteConfig.adminEmail"
         copyAdminOnNotifications:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: copyAdminOnNotifications
             label: "Copy Administrator on Notifications?"
             description: "Indicates whether the primary administrator should receive a copy of error, issue, new user, and update notifications if the administrator is not one of the configured recipients."
@@ -659,7 +659,7 @@ notifications:
             label: "Other"
 
         emailAllowNonuserSubscribers:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: emailAllowNonuserSubscribers
             label: "Allow Nonuser Subscribers"
             description: "Indicates whether this site should restrict email addresses for site notifications to addresses that are associated with valid active users of the XNAT installation. If turned on, the site is more secure from exploitation as a spam relay, but restricts the addresses that can be used when alerting administrators to system events."
@@ -715,15 +715,15 @@ authenticationMethods:
     contentType: json
     contents:
         xnatInternal:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":provider.providers.xnatInternal"
             label: XNAT (Internal)
         ldapProvider:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":provider.providers.ldap"
             label: LDAP
 #        oauthProvider:
-#            kind: panel.input.checkbox
+#            kind: panel.input.switchbox
 #            id: oauthProvider
 #            name: ":provider.providers.oauth"
 #            label: OAuth
@@ -811,7 +811,7 @@ registrationOptions:
     contentType: json
     contents:
         requireEmailVerificationToRegister:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: emailVerification
             label: "Require Email Verification <br>to Register?"
             description: >
@@ -830,7 +830,7 @@ registrationOptions:
             name: emailVerificationExpiration
             label: "Email Verification Expiration"
         autoEnableUserRegistration:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: userRegistration
             label: "Auto-enable <br>User Registration?"
             description: >
@@ -838,7 +838,7 @@ registrationOptions:
                 projects immediately. If false, the site administrator will be required to manually enable user accounts. Either way the administrator
                 receives an email notification when a user registers.
         autoEnablePar:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: par
             label: "Auto-enable with <br>Project Access Request?"
             description: >
@@ -847,7 +847,7 @@ registrationOptions:
                 access requests should override how user registration is normally handled. Either way the administrator receives an email notification
                 when a user registers.
         uiAllowNewUserComments:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: ":UI.allow-new-user-comments"
             label: "Allow User Comments <br>on Registration?"
 
@@ -906,7 +906,7 @@ anonymization:
     url: /xapi/siteConfig
     contents:
         enableSitewideAnonymizationScript:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: enableSitewideAnonymizationScript
             label: "Enable Site-wide <br>Anonymization Script?"
         sitewideAnonymizationScript:
@@ -926,7 +926,7 @@ seriesImportFilter:
     url: /xapi/siteConfig
     contents:
         enableSitewideSeriesImportFilter:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: enableSitewideSeriesImportFilter
             label: "Enable Site-wide <br>Series Import Filter?"
         sitewideSeriesImportFilterMode:
@@ -1013,7 +1013,7 @@ sessionUploadMethod:
 #            label: Display Applet Link
 #            description: "Enable to display link to Upload Applet on various XNAT pages."
         enableProjectAppletScript:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: enableProjectAppletScript
             label: Enable Project Applet Script
             description: "The site-wide applet settings script can be supplemented by applet settings specified at the project level if this setting is enabled."
@@ -1109,7 +1109,7 @@ automationSettings:
     url: /xapi/automation
     contents:
         internalScriptingEnabled:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: internalScriptingEnabled
             label: Internal Scripting Enabled
             description: "When enabled, administrators can create and edit scripts that run internally to the XNAT process. This can be a powerful feature, but also can pose security hazards that may be unacceptable for certain deployments. For these situations, configurable internal scripting can be disabled (XNAT itself may still use some scripting for feature implementation, but these scripts can not be modified or updated by users)."
@@ -1124,7 +1124,7 @@ misc:
     url: /xapi/siteConfig
     contents:
         scanTypeMapping:
-            kind: panel.input.checkbox
+            kind: panel.input.switchbox
             name: scanTypeMapping
             label: Scan Type Mapping?
         development:
diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js
index c602a2f0..c7842215 100644
--- a/src/main/webapp/scripts/xnat/ui/panel.js
+++ b/src/main/webapp/scripts/xnat/ui/panel.js
@@ -332,7 +332,6 @@ var XNAT = getObject(XNAT || {});
             _formPanel = spawn('form.xnat-form-panel.panel.panel-default', extend(true, {
                 id: toDashed(opts.id || opts.element.id || opts.name) + '-panel',
                 name: opts.name,
-                method: opts.method || 'POST',
                 action: opts.action ? XNAT.url.rootUrl(opts.action) : '#!',
                 addClass: opts.classes || '',
                 data: opts.data
@@ -349,6 +348,11 @@ var XNAT = getObject(XNAT || {});
 
             ]);
 
+        // add [method] attribute ONLY for POST or GET
+        if (/POST|GET/i.test(opts.method+'')) {
+            _formPanel.method = opts.method;
+        }
+
         // if there's a 'validation' (or 'validate') property, add 'validate' class
         if (opts.validation || opts.validate) {
             addClassName(_formPanel, 'validate');
@@ -523,7 +527,7 @@ var XNAT = getObject(XNAT || {});
                 method: $form.data('method') || opts.method || 'POST',
                 url: this.action,
                 success: function(){
-                    var obj = {},
+                    var obj = {}, callback,
                         _load = opts.refresh || opts.reload || opts.url || opts.load;
                     // actually, NEVER use returned data...
                     // ALWAYS reload from the server
@@ -533,6 +537,19 @@ var XNAT = getObject(XNAT || {});
                         XNAT.ui.banner.top(2000, 'Data saved successfully.', 'success');
                         loadData($form, obj);
                     }
+                    // fire callback function if specified
+                    if (opts.success || opts.callback) {
+                        callback = opts.success||opts.callback;
+                        if (typeof callback === 'string') {
+                            callback = eval(callback);
+                        }
+                        try {
+                            callback.apply(this, arguments);
+                        }
+                        catch(e) {
+                            console.log('something is broken: ' + e);
+                        }
+                    }
                 }
             };
 
@@ -710,7 +727,7 @@ var XNAT = getObject(XNAT || {});
             opts.element.id = (opts.id || opts.element.id) + '-element';
         }
         addClassName(opts.element, 'panel-element');
-        addDataObjects(opts.element, { name: opts.name||'' });
+        addDataObjects(opts.element, { name: (opts.name||'').replace(/^:*/, '') });
         opts.label = opts.label||opts.title||opts.name||'';
 
         // add a help info icon if one is specified
@@ -871,6 +888,13 @@ var XNAT = getObject(XNAT || {});
         return XNAT.ui.template.panelInput(opts).spawned;
     };
 
+    panel.input.switchbox = function panelInputSwitchbox(opts){
+        opts = cloneObject(opts);
+        opts.type = 'checkbox';
+        addClassName(opts, 'switchbox');
+        return XNAT.ui.template.panelInput(opts).spawned;
+    };
+
     panel.input.radio = function panelInputRadio(opts){
         opts = cloneObject(opts);
         opts.type = 'radio';
@@ -1249,7 +1273,6 @@ var XNAT = getObject(XNAT || {});
                 $(description).removeClass('hidden');
                 alert('foo');
             });
-            //button.onchange = ;
 
             label.append = description;
 
diff --git a/src/main/webapp/scripts/xnat/ui/select.js b/src/main/webapp/scripts/xnat/ui/select.js
index a2fafb71..2fe34c52 100644
--- a/src/main/webapp/scripts/xnat/ui/select.js
+++ b/src/main/webapp/scripts/xnat/ui/select.js
@@ -40,6 +40,12 @@ var XNAT = getObject(XNAT);
             return spawn('option', opt);    
         });      
     };
+
+
+    // ADD options to a menu
+    select.addOptions = function(menu, options){
+        $$(menu).append(select.options(options));
+    };
     
     
     // ========================================
@@ -55,13 +61,13 @@ var XNAT = getObject(XNAT);
         config.layout = firstDefined(config.layout, 'left');
 
         config.id = config.id || toDashed(config.name) || randomID('menu-', false);
-        config.name = config.name || toCamelCase(config.id) || '';
+        config.name = config.name || '';
 
         config.element = extend(true, {
             id: config.id,
             name: config.name,
             value: config.value || '',
-            title: config.title || config.name || config.id || ''
+            title: config.title || ''
         }, config.element);
 
         menu = spawn('select', config.element);
diff --git a/src/main/webapp/scripts/xnat/ui/templates.js b/src/main/webapp/scripts/xnat/ui/templates.js
index 2207c18a..9b3f1a5c 100644
--- a/src/main/webapp/scripts/xnat/ui/templates.js
+++ b/src/main/webapp/scripts/xnat/ui/templates.js
@@ -99,8 +99,9 @@ var XNAT = getObject(XNAT);
         var _templ, _spawn, _html;
         opts = cloneObject(opts);
         addClassName(opts, 'panel-element');
+        opts.name = (opts.name||'').replace(/^:*/,'');
         _templ = [
-            'div|data-name='+(opts.name||''),
+            'div|data-name='+opts.name,
             { className: opts.className },
             [].concat(content, spawn('br.clear'))
         ];
@@ -238,22 +239,36 @@ var XNAT = getObject(XNAT);
 
         // add value to [data-value] attribute
         // (except for textareas - that could get ugly)
-        if (isArray(element.value) || stringable(element.value)) {
-            $element.not('textarea').dataAttr('value', element.value);
+        if (!/textarea/i.test(element.tagName)){
+            if (isArray(element.value) || stringable(element.value)) {
+                $element.dataAttr('value', element.value);
+            }
         }
 
         var inner = [];
 
         // add 'before' content before the core element
         if (opts.beforeElement) {
-            opts.beforeElement = stringable(opts.beforeElement) ? [opts.beforeElement] : 
+            opts.beforeElement = stringable(opts.beforeElement) ? [opts.beforeElement] : '';
             inner.push(spawn('span.before', opts.beforeElement));
         }
 
-        inner.push(element);
+
+        // special stuff for switchbox elements
+        if (/switchbox/i.test(opts.kind)) {
+            inner.push(spawn('label.switchbox', [
+                element,
+                ['span.switchbox-outer', [['span.switchbox-inner']]]
+            ]))
+        }
+        else {
+            inner.push(element);
+        }
+
 
         // add 'after' content after the core element
         if (opts.afterElement) {
+            opts.afterElement = stringable(opts.afterElement) ? [opts.afterElement] : '';
             inner.push(spawn('span.after', opts.afterElement));
         }
 
diff --git a/src/main/webapp/style/app.css b/src/main/webapp/style/app.css
index 989d14e3..33d385a3 100644
--- a/src/main/webapp/style/app.css
+++ b/src/main/webapp/style/app.css
@@ -286,7 +286,7 @@ div.containerTitle {
 }
 
 div.containerBody {
-    overflow: auto;
+    /*overflow: auto;*/
     font-size: 12px;
     line-height: 15px;
     padding: 0;
@@ -324,7 +324,8 @@ div.mainContainerBody {
 
 div.containerItem {
     /* font-size: 11px ; line-height: 13px ; */
-    padding: /* 3px 0 0 3px */ 8px 12px 0 12px;
+    /*padding: !* 3px 0 0 3px *! 8px 12px 0 12px;*/
+    margin: 15px;
 }
 
 div.projectIndex {
@@ -1120,6 +1121,11 @@ select[multiple] {
     max-width: 200px;
 }
 
+/* Chosen menu overrides */
+body.xnat .chosen-container {
+    font-size: 12px;
+}
+
 /* page layout styles */
 #page_wrapper {
     width: 1160px;
@@ -1954,7 +1960,7 @@ span.noteOptional {
 }
 
 input.requiredField {
-    padding: 3px 5px 2px 4px;
+    /*padding: 3px 5px 2px 4px;*/
     border: 1px solid #cc0000;
 }
 
@@ -1976,6 +1982,11 @@ div.icon-remove {
 
 input.invalid {
     background: #ffc;
+    border: 1px solid #c00;
+}
+
+input.required.invalid {
+    background: #fcc;
 }
 
 .summary h3 {
@@ -2103,7 +2114,7 @@ input.invalid {
 /* ADD/EDIT PROJECT FORM */
 #new-project-form,
 #edit-project-form {
-    /* width: 760px; */
+    width: 960px;
     margin: 0 auto;
 }
 
@@ -3178,6 +3189,43 @@ label.search-method-label {
     white-space: nowrap;
 }
 
+/* "switch" checkbox widgets - pure css */
+label.switchbox > input { display: none; }
+
+label.switchbox > input + .switchbox-outer {
+    display: inline-block;
+    width: 32px;
+    height: 16px;
+    background: #c00;
+    position: relative;
+    border-radius: 3px;
+    border: 1px solid #a0a0a0;
+    box-shadow: inset 0 0 5px rgba(0,0,0,0.3);
+}
+
+label.switchbox > input + .switchbox-outer > .switchbox-inner {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    background: #fff;
+    border: none;
+    position: absolute;
+    right: auto;
+    top: 0;
+    border-radius: 1px;
+    left: 0;
+    box-shadow: 0 0 3px rgba(0,0,0,0.3);
+}
+
+label.switchbox > input:checked + .switchbox-outer {
+    background: #0a0;
+}
+
+label.switchbox > input:checked + .switchbox-outer > .switchbox-inner {
+    right: 0; left: auto;
+}
+
+
 /* friendlyForm styles. Taken from ConnectomeDB. To be refactored / replaced in XNAT 1.7, most likely */
 fieldset,
 .friendlyForm fieldset {
-- 
GitLab