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 26b041f209795872126d7eb0b6759798ae00eec6..ccc7dddaf53e1c329bf736f623f4a9c966c1856a 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 @@ -17,34 +17,68 @@ siteId: validation: required id onblur description: "The id used to refer to this site (also used to generate database ids). The Site ID must start with a letter and contain only letters, numbers and underscores. It should be a short, one-word name or acronym which describes your site. No spaces or non-alphanumeric characters." -siteDescriptionType: - kind: panel.display - id: siteDescriptionType - name: siteDescriptionType - label: Site Description - value: Select a site description option below - before: - - "<script type=\"text/javascript\" src=\"../../scripts/xnat/admin/siteInfo.js\"></script>" - after: - - "<span style=\"position: relative; top: -15px;left: 270px;\"><input type=\"radio\" name=\"siteDescriptionType\" id=\"siteDescriptionTypePage\" value=\"Page\"> Page <input type=\"radio\" name=\"siteDescriptionType\" id=\"siteDescriptionTypeText\" value=\"Text\"> Text (Markdown)</span>" - siteDescriptionPage: - kind: panel.input.text - id: siteDescriptionPage - name: siteDescriptionPage - label: " " - description: "Specify a velocity template file to display on the login page" + tag: input + element: + type: text + id: siteDescriptionPage + name: siteDescriptionPage + size: 50 + after: "<p>Specify a velocity template file to display on the login page</p>" siteDescriptionText: - kind: panel.textarea - id: siteDescriptionText - name: siteDescriptionText - label: " " - description: "Specify a simple text description of this site" + tag: textarea element: - style: - width: 300px - height: 150px + id: siteDescriptionText + name: siteDescriptionText + rows: 8 + after: "<p>Specify a simple text description of this site.</p>" + +siteDescriptionType: + kind: panel.element + #id: siteDescriptionType + #name: siteDescriptionType + label: Site Description + contents: + info: + tag: p + element: + style: "margin: 3px 0 8px 0;" + content: Select a site description option below + siteDescriptionTypePage: + tag: input|data-value=Page + element: + type: radio + name: siteDescriptionType + id: siteDescriptionTypePage + value: Page + after: + label: + tag: label.pad5h|for=siteDescriptionTypePage + content: Page + siteDescriptionTypeText: + tag: input|data-value=Text + element: + type: radio + name: siteDescriptionType + id: siteDescriptionTypeText + value: Text + after: + label: + tag: label.pad5h|for=siteDescriptionTypeText + content: Text (Markdown) + pagePath: + tag: div.input-bundle.page + contents: + ${siteDescriptionPage} + pageText: + tag: div.input-bundle.text + contents: + ${siteDescriptionText} + siteInfoJs: + tag: script + element: + src: ~/scripts/xnat/admin/siteInfo.js siteLoginLanding: kind: panel.input.text @@ -100,6 +134,8 @@ archivePath: label: Archive Path validation: required path description: "" + element: + disabled: true cachePath: kind: panel.input.text id: cachePath @@ -223,9 +259,7 @@ userLoginsSessionControls: id: sessionTimeout name: sessionTimeout label: Session Timeout - description: > - Interval for timing out user sessions. Uses - <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a> + description: "Interval for timing out user sessions. Uses <a target=\"_blank\" href=\"http://www.postgresql.org/docs/9.0/static/functions-datetime.html\">PostgreSQL interval notation</a>" aliasTokenTimeout: kind: panel.input.text id: aliasTokenTimeout @@ -617,7 +651,9 @@ themeManagement: action: /xapi/theme contents: themeScript: - tag: script|type=text/javascript;src=../../scripts/xnat/admin/themeManagement.js + tag: script + element: + src: ~/scripts/xnat/admin/themeManagement.js themeStyle: tag: style element: @@ -1011,8 +1047,11 @@ fileSystem: contentType: json action: /xapi/siteConfig/batch load: ?? XNAT.data.siteConfig - after: - - "<script type=\"text/javascript\">$('#archivePath').attr('disabled', 'disabled');</script>" +# after: +# disableArchivePath: +# tag: script +# element: +# html: "$$('id=archivePath').attr('disabled', 'disabled')" contents: ${archivePath} ${cachePath} @@ -1057,7 +1096,7 @@ misc: link: tag: a.link element: - href: */page/admin/spawner/ + href: ~/page/admin/spawner/ target: _blank html: Manage The Spawner swagger: @@ -1067,27 +1106,11 @@ misc: link: tag: a.link element: - href: */xapi/swagger-ui.html + href: ~/xapi/swagger-ui.html target: _blank html: Swagger -development: - kind: panel - name: development - label: Development Utilities - contents: - manageSpawner: - kind: panel.element - name: manageSpawner - label: Manage Spawner Elements - element: - html: > - <a href="/page/admin/spawner">Manage Spawner Elements</a> - - - - ################################################# #### Root Site Admin Spawner Config Object #### ################################################# diff --git a/src/main/webapp/page/admin/spawner/spawner-admin.js b/src/main/webapp/page/admin/spawner/spawner-admin.js index b9556b8ed925c278bc0481c243776e6d86b15152..b1feb5b87b151b58db2f5872a19f741404a8a830 100644 --- a/src/main/webapp/page/admin/spawner/spawner-admin.js +++ b/src/main/webapp/page/admin/spawner/spawner-admin.js @@ -129,12 +129,12 @@ XNAT.xhr.getJSON({ url: elementUrl, data: obj.$modal.find('textarea.yaml').val(), contentType: 'text/x-yaml', - // processData: false, + processData: false, success: function(){ xmodal.message('Data saved.') }, - error: function(e){ - xmodal.message('An error occured: ' + e) + error: function(e, f, g){ + xmodal.message(['<b>Error:</b>', e, f, g].join('<br>')) } }); } diff --git a/src/main/webapp/page/admin/style.css b/src/main/webapp/page/admin/style.css index 2c1ae87e51252afebe1b0ce12a99ac02360d2031..b2f6ed2c5a2fd671c8ddd044639a1ef7612e3652 100644 --- a/src/main/webapp/page/admin/style.css +++ b/src/main/webapp/page/admin/style.css @@ -92,6 +92,8 @@ body.xnat .panel-default { border: 1px solid #c8c8c8; } .panel .panel-element span.after { margin-left: 10px; } .panel .panel-element label.small { font-weight: normal; } +.panel .input-bundle { margin-top: 5px; } + .panel .panel-subhead { padding: 2px; margin: 10px; font-size: 13px; diff --git a/src/main/webapp/scripts/xnat/admin/siteInfo.js b/src/main/webapp/scripts/xnat/admin/siteInfo.js index 10c0ede2fe4560605ef96616e12a2cd4e97b9682..070fe46d03d318ae06b89a8bb1d42c68b9ebc837 100644 --- a/src/main/webapp/scripts/xnat/admin/siteInfo.js +++ b/src/main/webapp/scripts/xnat/admin/siteInfo.js @@ -1,31 +1,45 @@ -var sdtPage, sdtText; -setTimeout(function(){ - sdtPage = $('#siteDescriptionTypePage'); - sdtText = $('#siteDescriptionTypeText'); - sdtPage.click(changeSiteDescriptionType); - sdtText.click(changeSiteDescriptionType); - changeSiteDescriptionType(XNAT.data.siteConfig.siteDescriptionType); -}, 100); -function changeSiteDescriptionType(eventOrValue){ - var value = eventOrValue; - if(typeof eventOrValue === 'object'){ - if(eventOrValue.target.id == "siteDescriptionTypeText"){ - value = 'Text'; - } else { - value = 'Page'; - } +// interractions with 'Site Info' section of admin ui +(function(){ + + var sdtPage, sdtText; + + setTimeout(function(){ + sdtPage = $('#siteDescriptionTypePage'); + sdtText = $('#siteDescriptionTypeText'); + sdtPage.click(changeSiteDescriptionType); + sdtText.click(changeSiteDescriptionType); + changeSiteDescriptionType(XNAT.data.siteConfig.siteDescriptionType); + }, 100); + + function changeSiteDescriptionType(eventOrValue){ + + var value = eventOrValue; + + if (typeof eventOrValue === 'object') { + if (eventOrValue.target.id == "siteDescriptionTypeText") { + value = 'Text'; + } + else { + value = 'Page'; + } + } + + sdtText.val(value); + sdtPage.val(value); + + var text = $('div.input-bundle.text'); + var page = $('div.input-bundle.page'); + + if (value == 'Text') { + sdtText.prop('checked', true); + text.show(); + page.hide(); + } + else { + sdtPage.prop('checked', true); + page.show(); + text.hide(); + } } - sdtText.val(value); - sdtPage.val(value); - var text = $('#siteDescriptionText').parent().parent(); - var page = $('#siteDescriptionPage').parent().parent(); - if(value == 'Text'){ - sdtText.prop('checked',true); - text.show(); - page.hide(); - } else { - sdtPage.prop('checked',true); - page.show(); - text.hide(); - } -}; \ No newline at end of file + +})(); diff --git a/src/main/webapp/scripts/xnat/spawner.js b/src/main/webapp/scripts/xnat/spawner.js index 6918f517f609d071ac0ecf4d6ab035efbb7e91ed..dc4efb747ccd04985d9735b0d5d363c5e66ffdf3 100644 --- a/src/main/webapp/scripts/xnat/spawner.js +++ b/src/main/webapp/scripts/xnat/spawner.js @@ -34,7 +34,7 @@ var XNAT = getObject(XNAT); spawner.notSpawned = []; function setRoot(url){ - url = url.replace(/^([*.]\/+)/, '/'); + url = url.replace(/^([*~.]\/+)/, '/'); return XNAT.url.rootUrl(url) } @@ -50,7 +50,7 @@ var XNAT = getObject(XNAT); var kind, methodName, method, spawnedElement, $spawnedElement; // save the config properties in a new object - prop = getObject(prop); + prop = cloneObject(prop); prop.config = prop.config || prop.element || {}; @@ -79,9 +79,14 @@ var XNAT = getObject(XNAT); // do a raw spawn() if 'kind' is 'element' // or if there's a tag property if (kind === 'element' || prop.tag || prop.config.tag) { + + // pass 'content' (not contentS) property to add + // stuff directly to spawned element + prop.content = prop.content || prop.children || prop.inner || ''; + try { spawnedElement = - spawn(prop.tag || prop.config.tag || 'div', prop.config); + spawn(prop.tag || prop.config.tag || 'div', prop.config, prop.content); // jQuery's .append() method is // MUCH more robust and forgiving // than element.appendChild() @@ -141,8 +146,8 @@ var XNAT = getObject(XNAT); // spawn child elements from... // 'contents' or 'content' or 'children' or // a property matching the value of either 'contains' or 'kind' - if (prop.contains || prop.contents || prop.content || prop.children || prop[prop.kind]) { - prop.contents = prop[prop.contains] || prop.contents || prop.content || prop.children || prop[prop.kind]; + if (prop.contains || prop.contents || prop[prop.kind]) { + prop.contents = prop[prop.contains] || prop.contents || prop[prop.kind]; // if there's a 'target' property, put contents in there if (spawnedElement.target || spawnedElement.inner) { $spawnedElement = $(spawnedElement.target || spawnedElement.inner); @@ -150,19 +155,43 @@ var XNAT = getObject(XNAT); else { $spawnedElement = $(spawnedElement.element); } - $spawnedElement.append(_spawn(prop.contents).get()); + + // if a string, number, or boolean is passed as 'contents' + // just append that as-is (as a string) + if (stringable(prop.contents)) { + $spawnedElement.append(prop.contents+''); + } + else { + $spawnedElement.append(_spawn(prop.contents).get()); + } } + // Treat 'before' and 'after' just like 'contents' + // but insert the items 'before' or 'after' the main + // spawned (outer) element. This may have unintended + // consequences depending on the HTML structure of the + // spawned widget that has things 'before' or 'after' it. + if (prop.after) { - $frag.append(prop.after) + if (stringable(prop.after) || Array.isArray(prop.after)) { + $frag.append(prop.after) + } + else if (isPlainObject(prop.after)) { + $frag.append(_spawn(prop.after).get()) + } } - + if (prop.before) { - $frag.prepend(prop.before) + if (stringable(prop.before) || Array.isArray(prop.before)) { + $frag.prepend(prop.before) + } + else if (isPlainObject(prop.before)) { + $frag.prepend(_spawn(prop.before).get()) + } } - + // if there's a .load() method, fire that - if (isFunction(spawnedElement.load)) { + if (isFunction(spawnedElement.load||null)) { spawnedElement.load(); } diff --git a/src/main/webapp/style/app.css b/src/main/webapp/style/app.css index ee76cc001dcf71ffac5478b61acb9eca2dbdcaeb..5dc84a0f99fa7620b895d41badee3e6560811af0 100644 --- a/src/main/webapp/style/app.css +++ b/src/main/webapp/style/app.css @@ -3568,3 +3568,13 @@ textarea.xml-formatted { display: block; padding: 10px; margin: 10px; width: 95%; height: 95%; overflow-x: scroll; overflow-y: scroll; border: 1px solid #ccc; } + +/* utility classes */ +.pad1 { padding: 1px; } +.pad5 { padding: 5px; } +.pad10 { padding: 10px; } +.pad20 { padding: 20px; } +.pad1h { padding-left: 1px; padding-right: 1px; } +.pad5h { padding-left: 5px; padding-right: 5px; } +.pad10h { padding-left: 10px; padding-right: 10px; } +.pad20h { padding-left: 20px; padding-right: 20px; }