diff --git a/build.gradle b/build.gradle index 49b2b8fc0d8292d36c150c4de4950b64b68d8077..f249716621b2f3b054668ef2ab5547ff86caac54 100644 --- a/build.gradle +++ b/build.gradle @@ -301,8 +301,7 @@ dependencies { compile "org.nrg:ecat4xnat:1.7.0-SNAPSHOT" compile "org.nrg:DicomDB:4.1.0" compile "org.nrg:ExtAttr:4.1.0" -// compile "org.nrg:DicomEdit:4.2.1" - compile "org.nrg:DicomEdit:4.2.2" + compile "org.nrg:DicomEdit:4.2.3" compile "org.nrg:DicomImageUtils:${vXnat}" compile "org.nrg:DicomUtils:1.3.1" compile "org.nrg:PrearcImporter:${vXnat}" @@ -386,7 +385,7 @@ dependencies { compile "org.apache.httpcomponents:httpcore-nio:4.4.4" compile "org.codehaus.groovy:groovy-all:${vGroovy}" - compile "org.python:jython:${vJython}" + compile "org.python:jython-standalone:${vJython}" compile "net.sourceforge.saxon:saxon:${vSaxon}" compile "xalan:xalan:2.7.2" diff --git a/src/main/java/org/nrg/xapi/rest/event/EventHandlerApi.java b/src/main/java/org/nrg/xapi/rest/event/EventHandlerApi.java index e8717e550508f34b4d6553b5055354026c38996e..166549e9cb25cc9e06f77132994cd930eeb6741f 100644 --- a/src/main/java/org/nrg/xapi/rest/event/EventHandlerApi.java +++ b/src/main/java/org/nrg/xapi/rest/event/EventHandlerApi.java @@ -12,8 +12,9 @@ import org.nrg.automation.event.entities.AutomationFilters; import org.nrg.automation.services.impl.hibernate.HibernateAutomationEventIdsService; import org.nrg.automation.services.impl.hibernate.HibernateAutomationFiltersService; import org.nrg.framework.annotations.XapiRestController; +import org.nrg.framework.event.EventClass; import org.nrg.framework.event.Filterable; -import org.nrg.framework.utilities.Reflection; +import org.nrg.framework.utilities.BasicXnatResourceLocator; import org.nrg.xapi.model.event.EventClassInfo; import org.nrg.xdat.XDAT; import org.nrg.xdat.om.XnatProjectdata; @@ -22,11 +23,12 @@ import org.nrg.xdat.security.XDATUser; import org.nrg.xdat.security.helpers.Permissions; import org.nrg.xdat.security.services.RoleHolder; import org.nrg.xft.security.UserI; -import org.nrg.xnat.event.conf.EventPackages; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -44,6 +46,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Properties; /** * The Class EventHandlerApi. @@ -69,13 +72,6 @@ public class EventHandlerApi { @Autowired private HibernateAutomationFiltersService filtersService; - /** - * The event packages. - */ - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - @Autowired - private EventPackages eventPackages; - /** * Inits the this. */ @@ -183,7 +179,7 @@ public class EventHandlerApi { Collections.sort(valueList); filterableFields.put(autoFilters.getField(), valueList); } else { - for (String value : autoFilters.getValues()) { + for (final String value : autoFilters.getValues()) { final List<String> values = filterableFields.get(autoFilters.getField()); if (!values.contains(value)) { values.add(value); @@ -206,23 +202,28 @@ public class EventHandlerApi { */ private List<String> getEventClassList(List<AutomationEventIds> eventIdsList) { final List<String> classList = Lists.newArrayList(); - // ClassList should be pulled from available event classes rather than from events - if (eventPackages != null) { - for (final String pkg : eventPackages) { - try { - for (final Class<?> clazz : Reflection.getClassesForPackage(pkg)) { - if (AutomationEventImplementerI.class.isAssignableFrom(clazz) && !clazz.isInterface() && - !Modifier.isAbstract(clazz.getModifiers())) { - if (!classList.contains(clazz.getName())) { - classList.add(clazz.getName()); - } - } - } - } catch (ClassNotFoundException | IOException e) { - // Do nothing. - } - } - } + try { + for (final Resource resource : BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/events/*-event.properties")) { + final Properties properties = PropertiesLoaderUtils.loadProperties(resource); + if (!properties.containsKey(EventClass.EVENT_CLASS)) { + continue; + } + final String clssStr = properties.get(EventClass.EVENT_CLASS).toString(); + try { + final Class<?> clazz = Class.forName(clssStr); + if (AutomationEventImplementerI.class.isAssignableFrom(clazz) && !clazz.isInterface() && + !Modifier.isAbstract(clazz.getModifiers())) { + if (!classList.contains(clazz.getName())) { + classList.add(clazz.getName()); + } + } + } catch (ClassNotFoundException cex) { + _log.debug("Could not load class for class name (" + clssStr + ")"); + } + } + } catch (IOException e) { + _log.debug("Could not load event class properties resources (META-INF/xnat/*-event.properties)"); + } // I think for now we'll not pull from the database if we've found event classes. If the database // contains any thing different, it should only be event classes that are no longer available. if (classList.size() < 1) { diff --git a/src/main/java/org/nrg/xnat/configuration/ReactorConfig.java b/src/main/java/org/nrg/xnat/configuration/ReactorConfig.java index 27138d6cc5b2346a7ff67ef57123914de76c3023..d831d26cc2c0ac18405ca4c72617f6f4e5ae5750 100755 --- a/src/main/java/org/nrg/xnat/configuration/ReactorConfig.java +++ b/src/main/java/org/nrg/xnat/configuration/ReactorConfig.java @@ -26,12 +26,6 @@ public class ReactorConfig { return new XftItemEventListener(eventBus); } - @Bean - public EventPackages eventPackages() { - // NOTE: These should be treated as parent packages. All sub-packages should be searched - return new EventPackages(new HashSet<>(Arrays.asList(new String[]{"org.nrg.xnat.event", "org.nrg.xft.event", "org.nrg.xdat.event"}))); - } - /** * Env. * diff --git a/src/main/java/org/nrg/xnat/event/entities/ScriptLaunchRequestEvent.java b/src/main/java/org/nrg/xnat/event/entities/ScriptLaunchRequestEvent.java index ff474487300f6466cf9b1477b3ad27231163d383..e5d2d2a0c488abc3599e836ed9c255a5092d33e0 100644 --- a/src/main/java/org/nrg/xnat/event/entities/ScriptLaunchRequestEvent.java +++ b/src/main/java/org/nrg/xnat/event/entities/ScriptLaunchRequestEvent.java @@ -10,15 +10,16 @@ import org.nrg.automation.event.AutomationEventImplementerI; import org.nrg.automation.event.entities.AutomationCompletionEvent; import org.nrg.automation.event.entities.PersistentEvent; import org.nrg.framework.event.EventClass; -import org.nrg.framework.event.persist.PersistentEventImplementerI; + +import com.google.common.collect.Maps; /** * The Class AutomationLaunchRequestEvent. */ @Entity @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID") -@EventClass(displayName="Script Launch Request Event") -public class ScriptLaunchRequestEvent extends PersistentEvent implements PersistentEventImplementerI, AutomationEventImplementerI { +@EventClass(name="ScriptLaunchRequestEvent", description="Script Launch Request Event") +public class ScriptLaunchRequestEvent extends PersistentEvent implements AutomationEventImplementerI { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 7465778737330635218L; @@ -26,7 +27,7 @@ public class ScriptLaunchRequestEvent extends PersistentEvent implements Persist /** The automation completion event. */ private AutomationCompletionEvent automationCompletionEvent; - private Map<String,Object> parameterMap; + private Map<String,Object> parameterMap = Maps.newHashMap(); /** * Instantiates a new automation launch request event. diff --git a/src/main/java/org/nrg/xnat/event/listeners/AutomationEventScriptHandler.java b/src/main/java/org/nrg/xnat/event/listeners/AutomationEventScriptHandler.java index 6f0d32a1cca965653e26b03c6a342bffa06c2a30..b5e4be6171514a8277d89c3851e6f9e15cf60a76 100644 --- a/src/main/java/org/nrg/xnat/event/listeners/AutomationEventScriptHandler.java +++ b/src/main/java/org/nrg/xnat/event/listeners/AutomationEventScriptHandler.java @@ -22,6 +22,7 @@ import org.nrg.framework.constants.Scope; import org.nrg.framework.event.Filterable; import org.nrg.framework.event.persist.PersistentEventImplementerI; import org.nrg.framework.exceptions.NrgServiceException; +import org.nrg.framework.exceptions.NrgServiceRuntimeException; import org.nrg.framework.services.NrgEventService; import org.nrg.xdat.XDAT; import org.nrg.xdat.security.helpers.Users; @@ -444,14 +445,23 @@ public class AutomationEventScriptHandler implements Consumer<Event<AutomationEv if (PersistentWorkflowUtils.IN_PROGRESS.equals(workflow.getStatus())) { WorkflowUtils.complete(workflow, workflow.buildEvent()); } - } catch (NrgServiceException e) { - final String message = String.format("Failed running the script %s by user %s for event %s on data type %s instance %s from project %s", + } catch (NrgServiceException | NrgServiceRuntimeException e) { + final String message = String.format("Failed running the script %s by user %s for event %s on data type %s instance %s from project %s (Exception=%s)", request.getScriptId(), request.getUser().getLogin(), request.getEvent(), request.getDataType(), request.getDataId(), - request.getExternalId()); + request.getExternalId(), + e.toString()); + if (scriptOut==null) { + scriptOut = new ScriptOutput(); + scriptOut.setStatus(Status.ERROR); + scriptOut.setOutput(message); + } + if (PersistentWorkflowUtils.IN_PROGRESS.equals(workflow.getStatus())) { + WorkflowUtils.fail(workflow, workflow.buildEvent()); + } AdminUtils.sendAdminEmail("Script execution failure", message); logger.error(message, e); if (PersistentWorkflowUtils.IN_PROGRESS.equals(workflow.getStatus())) { diff --git a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java index 7cc14d329c90596d45c01caa758d47becb2963a4..6bca03b5a2bf491b32dc29a83aceb0a328c3331d 100644 --- a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java +++ b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java @@ -69,7 +69,7 @@ public class SecurityConfig { @Bean public XnatAuthenticationEntryPoint loginUrlAuthenticationEntryPoint() { - final XnatAuthenticationEntryPoint entryPoint = new XnatAuthenticationEntryPoint("/app/template/Login.vm"); + final XnatAuthenticationEntryPoint entryPoint = new XnatAuthenticationEntryPoint("/app/template/Login.vm", _configuration); entryPoint.setDataPaths(Arrays.asList("/xapi/**", "/data/**", "/REST/**", "/fs/**")); entryPoint.setInteractiveAgents(Arrays.asList(".*MSIE.*", ".*Mozilla.*", ".*AppleWebKit.*", ".*Opera.*")); return entryPoint; @@ -100,8 +100,7 @@ public class SecurityConfig { RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy = new RegisterSessionAuthenticationStrategy(sessionRegistry); authStrategies.add(registerSessionAuthenticationStrategy); - final CompositeSessionAuthenticationStrategy compositeSessionAuthenticationStrategy = new CompositeSessionAuthenticationStrategy(authStrategies); - return compositeSessionAuthenticationStrategy; + return new CompositeSessionAuthenticationStrategy(authStrategies); } @Bean diff --git a/src/main/java/org/nrg/xnat/security/XnatAuthenticationEntryPoint.java b/src/main/java/org/nrg/xnat/security/XnatAuthenticationEntryPoint.java index 3c08027aa4f2df48283a2f49a53f6133d101458b..1c40b2b9f1aa27bff5000df7a4b61f55e6943f8b 100644 --- a/src/main/java/org/nrg/xnat/security/XnatAuthenticationEntryPoint.java +++ b/src/main/java/org/nrg/xnat/security/XnatAuthenticationEntryPoint.java @@ -13,6 +13,8 @@ package org.nrg.xnat.security; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.nrg.config.exceptions.SiteConfigurationException; +import org.nrg.xdat.preferences.InitializerSiteConfiguration; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -28,8 +30,9 @@ import java.util.regex.Pattern; public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { - public XnatAuthenticationEntryPoint(String loginFormUrl) { + public XnatAuthenticationEntryPoint(final String loginFormUrl, final InitializerSiteConfiguration configuration) { super(loginFormUrl); + _configuration = configuration; } /** @@ -41,8 +44,9 @@ public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoi * @param request HTTP request object. * @param response HTTP response object. * @param authException An authentication exception that may have redirected the agent to re-authenticate. - * @throws IOException - * @throws ServletException + * + * @throws IOException When an error occurs reading or writing data. + * @throws ServletException When an error occurs in the framework. */ @Override public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException { @@ -51,27 +55,33 @@ public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoi if (_log.isDebugEnabled()) { _log.debug("Evaluating data path request: " + strippedUri + ", user agent: " + userAgent); - } - - if(!StringUtils.isBlank(strippedUri) && strippedUri.contains("/action/AcceptProjectAccess/par/")) { - int index=strippedUri.indexOf("/par/")+5; - if(strippedUri.length()>index){//par number included? - String parS=strippedUri.substring(index); - if(parS.contains("/")){ - parS=parS.substring(0,parS.indexOf("/")); - } - - request.getSession().setAttribute("par", parS); - } } - - if (isDataPath(request) && !isInteractiveAgent(userAgent)) { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return; + + if (!StringUtils.isBlank(strippedUri) && strippedUri.contains("/action/AcceptProjectAccess/par/")) { + int index = strippedUri.indexOf("/par/") + 5; + if (strippedUri.length() > index) {//par number included? + String parS = strippedUri.substring(index); + if (parS.contains("/")) { + parS = parS.substring(0, parS.indexOf("/")); + } + + request.getSession().setAttribute("par", parS); + } } + if (isDataPath(request) && !isInteractiveAgent(userAgent)) { + try { + response.setHeader("WWW-Authenticate", "Basic realm=\"" + _configuration.getSiteId() + "\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } catch (SiteConfigurationException e) { + _log.error("An error occurred trying to access system resources: siteId", e); + response.setHeader("WWW-Authenticate", "Basic"); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + } else { super.commence(request, response, authException); } + } /** * Sets the data paths, i.e. those paths which require a user-agent interactivity test to determine whether the user @@ -83,7 +93,7 @@ public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoi public void setDataPaths(final List<String> dataPaths) { if (_log.isDebugEnabled()) { _log.debug("Adding " + dataPaths + " data paths"); - } + } for (final String dataPath : dataPaths) { if (_log.isDebugEnabled()) { @@ -102,7 +112,7 @@ public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoi for (final String interactiveAgent : interactiveAgents) { if (_log.isDebugEnabled()) { _log.debug("Adding interactive agent specifier: " + interactiveAgent); -} + } final Pattern pattern = Pattern.compile(interactiveAgent); _agentPatterns.add(pattern); } @@ -146,8 +156,10 @@ public class XnatAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoi return false; } + private final InitializerSiteConfiguration _configuration; + private static final Log _log = LogFactory.getLog(XnatAuthenticationEntryPoint.class); - private final List<RequestMatcher> _dataPaths = new ArrayList<>(); - private final List<Pattern> _agentPatterns = new ArrayList<>(); + private final List<RequestMatcher> _dataPaths = new ArrayList<>(); + private final List<Pattern> _agentPatterns = new ArrayList<>(); } diff --git a/src/main/java/org/nrg/xnat/services/messaging/automation/AutomatedScriptRequest.java b/src/main/java/org/nrg/xnat/services/messaging/automation/AutomatedScriptRequest.java index 1512ed44f62fc7e25bf70c2e19683be229d2565d..c6204cebb86d50eac498735689fe7e80ecd4f789 100644 --- a/src/main/java/org/nrg/xnat/services/messaging/automation/AutomatedScriptRequest.java +++ b/src/main/java/org/nrg/xnat/services/messaging/automation/AutomatedScriptRequest.java @@ -87,7 +87,9 @@ public class AutomatedScriptRequest implements Serializable { */ public AutomatedScriptRequest(final String srcEventId, final String srcEventClass, final UserI user, final String scriptId, final String event, final String scriptWorkflow, final String dataType, final String dataId, final String externalId, Map<String,Object> argumentMap) { this(srcEventId, srcEventClass, user, scriptId, event, scriptWorkflow, dataType, dataId, externalId); - _argumentMap.putAll(argumentMap); + if (argumentMap != null) { + _argumentMap.putAll(argumentMap); + } } /** diff --git a/src/main/webapp/WEB-INF/tags/page/xnat.tag b/src/main/webapp/WEB-INF/tags/page/xnat.tag index 5509eb107147fac81d6c998c919ef9a1c9d6780f..df3ab8e8e0ecfdd3d300f79acb682a80701b44f2 100644 --- a/src/main/webapp/WEB-INF/tags/page/xnat.tag +++ b/src/main/webapp/WEB-INF/tags/page/xnat.tag @@ -359,36 +359,11 @@ ${bodyTop} <!-- search script --> <script type="text/javascript"> - <!-- - function DefaultEnterKey(e, button){ - var keynum, keychar, numcheck; - - if (window.event) // IE - { - keynum = e.keyCode; - if (keynum == 13) { - submitQuickSearch(); - return true; - } - } - else if (e) // Netscape/Firefox/Opera - { - keynum = e.which; - if (keynum == 13) { - submitQuickSearch(); - return false; - } - } - return true; - } - function submitQuickSearch(){ - concealContent(); - if (document.getElementById('quickSearchForm').value != "") - document.getElementById('quickSearchForm').submit(); + if($('#searchValue').val()!="") { + $('#quickSearchForm').submit(); + } } - - //--> </script> <!-- end search script --> @@ -426,11 +401,20 @@ ${bodyTop} </optgroup> </select> <input id="searchValue" class="clean" name="searchValue" type="text" maxlength="40" size="20" value=""> + <input id="xnat_csrf" name="XNAT_CSRF" type="hidden" value=""> <button type="button" id="search_btn" class="btn2" onclick="submitQuickSearch();">Go</button> <script> - - $('#searchValue').each(function(){ + var searchField = $('#searchValue'); + searchField.keyup(function( event ) { + if (event.which == 13) { + submitQuickSearch(); + } + }); + + $('#xnat_csrf').val(window.csrfToken); + + searchField.each(function(){ var _this = this; _this.value = _this.value || 'search'; $(_this).focus(function(){ diff --git a/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js b/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js index 77b4dd7cb1a981747ba8fa801891aaddf6900e2c..8e593ec50c7af1d4515eff3da2637891a6987875 100644 --- a/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js +++ b/src/main/webapp/scripts/uploaders/AutomationBasedUploader.js @@ -270,8 +270,12 @@ XNAT.app.abu.eventHandlerChange = function(){ var eventHandler = $('#eventHandlerSelect').val(); if (typeof eventHandler === 'undefined' || eventHandler == null || eventHandler.length<1 && ($("#handlerDefaultOption").html() == 'NONE DEFINED' || $("#handlerDefaultOption").html() == 'SELECT')) { $("#abu-process-button").addClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","hidden"); } else if ((XNAT.app.abu.usageSelect=='Launch') || (abu._fileUploader._uploadStarted && abu._fileUploader._filesInProgress<1)) { $("#abu-process-button").removeClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","visible"); } XNAT.app.abu.filesProcessed = false; } @@ -411,7 +415,7 @@ XNAT.app.abu.populateEventHandlerSelect = function(){ if ($('#eventHandlerSelect').find('option').length==1) { $('#handlerDefaultOption').html('NONE DEFINED'); } else if ($('#eventHandlerSelect').find('option').length==2) { - $('#eventHandlerSelect').find('option').get(0).remove(); + $($('#eventHandlerSelect').find('option').get(0)).remove(); } } else { $('#eventHandlerSelect').append('<option value="">NONE DEFINED</option>'); @@ -591,6 +595,8 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){ var eventHandler = $('#eventHandlerSelect').val(); if (typeof eventHandler !== 'undefined' && eventHandler != null && eventHandler.length>0) { $("#abu-process-button").removeClass("abu-button-disabled"); + $("#abu-process-button-text").html("Process Files"); + $("#abu-process-button").css("visibility","visible"); } else { $("#abu-done-button-text").html("Done"); $("#abu-done-button").removeClass("abu-button-disabled"); @@ -615,6 +621,7 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){ var eventHandler = $('#eventHandlerSelect').val(); if (eventHandler != undefined && eventHandler != null && eventHandler.length>0) { $("#abu-process-button").removeClass("abu-button-disabled"); + $("#abu-process-button").css("visibility","visible"); } else { $("#abu-done-button").removeClass("abu-button-disabled"); $("#abu-done-button-text").html("Done"); @@ -628,6 +635,8 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){ $("#abu-done-button-text").html("Cancel"); if ($('#eventHandlerSelect option').size()>1 && $('#eventHandlerSelect').val()=="") { $("#abu-process-button").addClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","hidden"); } $("#file-uploader-instructions-sel").css("display","none"); } else { @@ -637,6 +646,8 @@ XNAT.app.abu.initializeAbuUploader = function(usageType){ abu._fileUploader.DRAG_AND_DROP_ON = false; } $("#abu-process-button").addClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","hidden"); $(".upload-area").css("display","none"); $(".eventhandler-area").css("display","none"); } @@ -662,8 +673,9 @@ XNAT.app.abu.usageSelectAction = function(){ $("#abu-upload-button").removeClass("abu-button-disabled"); abu._fileUploader.DRAG_AND_DROP_ON = true; $("#abu-process-button").addClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","hidden"); $("#script-select-text").html("Post-upload processing script:"); - $("#abu-process-button-text").html("Process files"); $("#resourceSelect").prop('disabled',false); $(".response_text").html(''); } else if (XNAT.app.abu.usageSelect=='Launch') { @@ -674,6 +686,7 @@ XNAT.app.abu.usageSelectAction = function(){ var eventHandler = $('#eventHandlerSelect').val(); if (eventHandler != undefined && eventHandler != null && eventHandler.length>0) { $("#abu-process-button").removeClass("abu-button-disabled"); + $("#abu-process-button").css("visibility","visible"); } $("#script-select-text").html("Script to launch:"); $("#abu-process-button-text").html("Run script"); @@ -716,6 +729,8 @@ XNAT.app.abu.whatToDoChange = function(){ $('#eventHandlerSelect').val(launchSelect); if (typeof abu !== 'undefined' && abu._fileUploader.uploadsStarted>0 && abu._fileUploader.uploadsInProgress==0) { $("#abu-process-button").removeClass("abu-button-disabled"); + $("#abu-process-button-text").html("Process files"); + $("#abu-process-button").css("visibility","visible"); } if (XNAT.app.abu.usageSelect == 'Upload' && $('#whatToDoSelect option').size()>1 && $('#whatToDoSelect').val()=="") { $("#abu-upload-button").addClass("abu-button-disabled"); @@ -903,9 +918,9 @@ XNAT.app.abu.processFiles=function() { ((this.paramsToPass.length>0) ? "Please supply values for the following parameters:" : "Please supply a value for the following parameter:") + ' </h3><div style="width:100px"><table>' + paramText + "</table>"); // Not sure why the setTimeout seems necessary. - $(".passParamInput").get(0).focus(); + $($(".passParamInput").get(0)).focus(); setTimeout(function(){ - $(".passParamInput").get(0).focus(); + $($(".passParamInput").get(0)).focus(); },100); } else { diff --git a/src/main/webapp/scripts/uploaders/fileuploader.js b/src/main/webapp/scripts/uploaders/fileuploader.js index e47a44d348a4c7b90d5d5d876c0db24978673f2e..c00698bc354151bc9f30a0f83551af2c6e58f928 100644 --- a/src/main/webapp/scripts/uploaders/fileuploader.js +++ b/src/main/webapp/scripts/uploaders/fileuploader.js @@ -27,7 +27,7 @@ abu.FileUploader = function(o){ $(this._options.element).append( '<div class="abu-uploader">' + '<div id="abu-files-processing" class="abu-files-processing"> Processing...... </div>' + - '<a id="file-uploader-instructions-sel" class="abu-uploader-instructions-sel" onclick="abu._fileUploader.uploaderHelp()">Click here for help.</a>' + + '<a id="file-uploader-instructions-sel" class="abu-uploader-instructions-sel" onclick="abu._fileUploader.uploaderHelp()">?</a>' + '<div class="abu-upload-drop-area" style="display: none;"><span>Drop files here to upload</span></div>' + '<div class="abu-xnat-interactivity-area">' + '</div>' + @@ -96,14 +96,35 @@ abu.FileUploader = function(o){ }); if (this.ALLOW_DRAG_AND_DROP) { + $(".abu-upload-drop-area").on('dragleave',function(e) { + if (this.DRAG_AND_DROP_ON) { + this.showDrag = false; + if (typeof this.timeout !== "undefined") { + clearTimeout( this.timeout ); + } + this.timeout = setTimeout( function(){ + if( !this.showDrag ){ + $(".abu-upload-drop-area").css('display','none'); + $(".abu-upload-drop-area").removeClass('abu-upload-drop-area-active'); + try { + e.preventDefault(); + e.stopPropogation(); + } catch(e) { /* Do nothing */ } + } + }.bind(this), 200 ).bind(this); + } + }.bind(this) + ); $(".abu-upload-drop-area").on('dragover',function(e) { if (this.DRAG_AND_DROP_ON) { + this.showDrag = true; this.activateUploadArea(e); } }.bind(this) ); $(".abu-upload-drop-area").on('dragenter',function(e) { if (this.DRAG_AND_DROP_ON) { + this.showDrag = true; this.activateUploadArea(e); } }.bind(this) @@ -120,14 +141,35 @@ abu.FileUploader = function(o){ } }.bind(this) ); + $(this._options.element).on('dragleave',function(e) { + if (this.DRAG_AND_DROP_ON) { + this.showDrag = false; + if (typeof this.timeout !== "undefined") { + clearTimeout( this.timeout ); + } + this.timeout = setTimeout( function(){ + if( !this.showDrag ){ + $(".abu-upload-drop-area").css('display','none'); + $(".abu-upload-drop-area").removeClass('abu-upload-drop-area-active'); + try { + e.preventDefault(); + e.stopPropogation(); + } catch(e) { /* Do nothing */ } + } + }.bind(this), 200 ).bind(this); + } + }.bind(this) + ).bind(this); $(this._options.element).on('dragover',function(e) { if (this.DRAG_AND_DROP_ON) { + this.showDrag = true; this.activateUploadArea(e); } }.bind(this) ).bind(this); $(this._options.element).on('dragenter',function(e) { if (this.DRAG_AND_DROP_ON) { + this.showDrag = true; this.activateUploadArea(e); } }.bind(this) @@ -145,9 +187,10 @@ abu.FileUploader = function(o){ this.processingComplete = function() { $("#abu-done-button-text").html("Done"); - //$("#abu-process-button").css("display","None"); //$("#abu-upload-button").css("display","None"); $("#abu-process-button").addClass("abu-button-disabled"); + //$("#abu-process-button-text").html(" "); + $("#abu-process-button").css("visibility","hidden"); $("#abu-upload-button").addClass("abu-button-disabled"); $("#abu-files-processing").css("display","None"); } @@ -292,14 +335,16 @@ abu.FileUploader = function(o){ '<div id="file-uploader-instructions" class="abu-uploader-instructions">' + '<h3>Instructions</h3>' + '<ul>' + - '<li>To upload, click the <b>Upload Files</b> button or drag files into the space below the buttons. (Drag-and-drop is supported in FF, Chrome.)</li>' + + ((this.ALLOW_DRAG_AND_DROP) ? + '<li>To upload, click the <b>Upload Files</b> button or drag files into the space below the buttons. (Drag-and-drop is supported in FF, Chrome.)</li>' : + '<li>To upload, click the <b>Upload Files</b> to begin selection of files for upload.</li>') + ((this._options.maxFiles == 1) ? '<li>This uploader supports only a single file upload</li>' : '<li>Multiple files may be selected</li>' ) + '<li>Uploads will begin automatically</li>' + '<li>Upload of directories is not supported</li>' + - '<li>When finished uploading, press <b>Process Files</b> to process the uploaded files</li>' + + '<li>When finished uploading, press <b>Done</b> to close the modal, or, if an automation script is to be launched by this upload process, press <b>Process Files</b> to process the uploaded files.</li>' + '</ul>' + '</div>'; xmodal.message("Uploader Instructions",templateV, undefined, {height:"400px",width:"800px"}); diff --git a/src/main/webapp/scripts/xnat/admin/themeManagement.js b/src/main/webapp/scripts/xnat/admin/themeManagement.js index 930e230b9ced35ee49cde6d64c1d982e8f3d70bf..64b3702a8b2137fbd04e3df7661aecb9238a67ae 100644 --- a/src/main/webapp/scripts/xnat/admin/themeManagement.js +++ b/src/main/webapp/scripts/xnat/admin/themeManagement.js @@ -84,8 +84,8 @@ function removeTheme(){ /*** Theme Package Upload Functions ***/ themeUploadForm.action = themeUrl+q+csrf; -$(themeUploadForm).parent().css('position','relative'); -$(themeUploadForm).parent().css('top','-30px'); +$(themeUploadForm).parent().parent().css('position','relative'); +$(themeUploadForm).parent().parent().css('top','-30px'); themeUploadForm.onsubmit = function(event) { event.preventDefault(); $(themeUploadSubmit).text('Uploading...'); @@ -120,7 +120,7 @@ themeUploadForm.onsubmit = function(event) { uploaded = true; } if(!uploaded){ - xmodal.message('Nothing Uploaded', 'No valid theme package files were selected for upload.'); + xmodal.message('Nothing Uploaded', 'No valid theme package files were selected for upload.<br><br>Click the "Choose Files" button below to browse for a theme package.'); $(themeUploadSubmit).text('Upload'); $(themeUploadSubmit).removeAttr('disabled'); } diff --git a/src/main/webapp/scripts/xnat/app/timeout.js b/src/main/webapp/scripts/xnat/app/timeout.js index 96315e3a682826028f12b8352e614046cd84e723..04bb9561bd3213f6d778a6169220b2e61f1bad77 100644 --- a/src/main/webapp/scripts/xnat/app/timeout.js +++ b/src/main/webapp/scripts/xnat/app/timeout.js @@ -272,7 +272,7 @@ var XNAT = getObject(XNAT); // need to wait a little longer before reloading setTimeout(function(){ window.location.reload(); - }, 120000); + }, 2000); } diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js index 410aeb12a34e6f0957d271ccf597b651f9d7e332..6ef65f211c7f13beaed985ba7155821dd3614f1d 100644 --- a/src/main/webapp/scripts/xnat/ui/panel.js +++ b/src/main/webapp/scripts/xnat/ui/panel.js @@ -426,7 +426,8 @@ var XNAT = getObject(XNAT || {}); } var ajaxConfig = { - method: opts.method, + //method: opts.method, + method: $form.data('method') || opts.method || 'POST', url: this.action, success: function(){ var obj = {}; @@ -845,54 +846,10 @@ var XNAT = getObject(XNAT || {}); panel.data = {}; panel.data.table = function(opts){ + // initialize the table - opts = cloneObject(opts); - opts.element = opts.element || {}; - addClassName(opts.element, 'data-table xnat-table'); - if (opts.sortable) { - if (opts.sortable === true) { - addClassName(opts.element, 'sortable'); - } - else { - opts.sortable = opts.sortable.split(',').map(function(item){return item.trim()}); - } - } - opts.element.style = { - width: opts.width || '100%' - }; - var dataTable = XNAT.table(opts.element); - // request data for table rows - XNAT.xhr.get({ - url: XNAT.url.rootUrl(opts.load||opts.url), - dataType: opts.dataType || 'json', - success: function(data){ - var props = []; - if (opts.items) { - dataTable.tr(); - forOwn(opts.items, function(name, val){ - props.push(name); - dataTable.th(val); - if (opts.sortable === true || opts.sortable.indexOf(name) !== -1) { - addClassName(dataTable.last.th, 'sort'); - } - }); - } - else { - forOwn(data[0], function(name, val){ - props.push(name); - }); - } - data.forEach(function(item){ - dataTable.tr(); - props.forEach(function(name){ - dataTable.td({ className: name }, item[name]); - }); - }); - if (opts.container) { - $$(opts.container).append(dataTable.table); - } - } - }); + var dataTable = XNAT.table.dataTable(opts.data||[], opts); + return { element: dataTable.table, spawned: dataTable.table, @@ -900,6 +857,7 @@ var XNAT = getObject(XNAT || {}); return dataTable.table } }; + }; panel.data.list = function(opts){ diff --git a/src/main/webapp/scripts/xnat/ui/table.js b/src/main/webapp/scripts/xnat/ui/table.js index 5a645866d1561676b29942bc306b2e242645dff0..ec651f9445b3086c73c0b42e8f414aa715d4f0b4 100755 --- a/src/main/webapp/scripts/xnat/ui/table.js +++ b/src/main/webapp/scripts/xnat/ui/table.js @@ -275,6 +275,7 @@ var XNAT = getObject(XNAT); else { obj = data || {}; } + if (obj.header) { // if there's a 'header' property // set to true, pick the header from @@ -347,15 +348,103 @@ var XNAT = getObject(XNAT); // helper for future XNAT DataTable widget table.dataTable = function(data, opts){ + var tableData = data; + // tolerate reversed arguments if (Array.isArray(opts)){ tableData = opts; - opts = data; + opts = getObject(data); } - addClassName(opts, 'xnat-table data-table'); - var newTable = new Table(opts); - return newTable.init(tableData); + + // don't modify original object + opts = cloneObject(opts); + + var allItems = opts.header || (opts.items && opts.items === 'all'); + + // properties for spawned element + opts.element = opts.element || {}; + + addClassName(opts.element, 'data-table xnat-table'); + + if (opts.sortable) { + if (opts.sortable === true) { + addClassName(opts.element, 'sortable'); + } + else { + opts.sortable = opts.sortable.split(',').map(function(item){return item.trim()}); + } + } + + opts.element = extend(true, { + style: { + width: opts.width || '100%' + } + }, opts.element); + + // initialize the table + var newTable = new Table(opts.element); + + function createTable(rows){ + var props = []; + if (!allItems && (opts.items || opts.properties)) { + newTable.tr(); + forOwn(opts.items||opts.properties, function(name, val){ + props.push(name); + newTable.th(val); + if (!opts.sortable) return; + if (opts.sortable === true || opts.sortable.indexOf(name) !== -1) { + addClassName(newTable.last.th, 'sort'); + } + }); + } + else { + if (allItems) { + newTable.tr(); + } + forOwn(rows[0], function(name, val){ + if (allItems) { + newTable.th(name); + } + props.push(name); + }); + } + rows.forEach(function(item){ + newTable.tr(); + props.forEach(function(name){ + newTable.td({ className: name }, item[name]); + }); + }); + } + + // if 'tableData' is a string, use as the url + if (typeof tableData == 'string') { + opts.url = tableData; + } + + // request data for table rows + if (opts.load || opts.url) { + XNAT.xhr.get({ + url: XNAT.url.rootUrl(opts.load||opts.url), + dataType: opts.dataType || 'json', + success: function(json){ + // handle data returned in ResultSet.Result array + json = (json.ResultSet && json.ResultSet.Result) ? json.ResultSet.Result : json; + createTable(json); + } + }); + } + else { + createTable(tableData.data||tableData); + // newTable.init(tableData); + } + + if (opts.container) { + $$(opts.container).append(newTable.table); + } + + return newTable; + }; // table with <input> elements in the cells @@ -395,4 +484,3 @@ var XNAT = getObject(XNAT); XNAT.ui.inputTable = XNAT.inputTable = table.inputTable; })); - diff --git a/src/main/webapp/scripts/xnat/ui/tabs.js b/src/main/webapp/scripts/xnat/ui/tabs.js index 61bec657552be5fde90b963b8a7246cf0d4bf80d..73eaf37aa06abe936f9f260c154a195e415d0bba 100755 --- a/src/main/webapp/scripts/xnat/ui/tabs.js +++ b/src/main/webapp/scripts/xnat/ui/tabs.js @@ -171,9 +171,9 @@ var XNAT = getObject(XNAT || {}); // set container and layout before spawning: // XNAT.tabs.container = 'div.foo'; - container = tabs.container || 'div.xnat-tab-container'; + container = obj.container || tabs.container || 'div.xnat-tab-container'; - layout = tabs.layout || 'left'; + layout = obj.layout || tabs.layout || 'left'; navTabs = spawn('div.xnat-nav-tabs'); tabContent = spawn('div.xnat-tab-content'); diff --git a/src/main/webapp/style/uploaders/fileuploader.css b/src/main/webapp/style/uploaders/fileuploader.css index 9d74966143518c36c510c409035f0160c92ecf98..fe323e24d1c0f126c3b975e2cfaea3ff59b3d952 100644 --- a/src/main/webapp/style/uploaders/fileuploader.css +++ b/src/main/webapp/style/uploaders/fileuploader.css @@ -85,13 +85,14 @@ div.abu-xnat-interactivity-area-sub { .abu-upload-duplicate {display:inline;color:#008800} .abu-upload-duplicate.abu-upload-duplicate-text {display:inline;color:#008800} -.abu-uploader-instructions-sel {margin-bottom:0px; margin-right:10px; display:inline; width:100%; float:right; background-color:#CCCCCC; border-color:#1A75BB; color:#1A75BB; border-style:solid; border-width:1px; border-color:#1A75BB; font-size:14px; font-weight: bold; padding:2px; } +.abu-uploader-instructions-sel {margin-bottom:0px; margin-right:10px; display:inline; float:right; background-color:#CCCCCC; border-style:solid; border-width:1px; width:auto; font-size:14px; font-weight: bold; padding:2px; } + .abu-uploader-instructions {margin-bottom:10px; display:inline} .abu-return-floatmessage {position:absolute;z-index:10001;width:auto;cursor:pointer;background-color:#FFFFCC; } .abu-return-message {width:auto;cursor:pointer;background-color:#FFFFCC; } -.abu-options-div { position: relative; float: left; width: 430px; margin-left: 10px; } +.abu-options-div { position: relative; float: left; width: 420px; margin-left: 10px; } .abu-options-cb { display:block; /* or inline-block */ width: auto; padding: 7px 0; text-align:center; margin-left:10px; float:left; padding-top: 0px; padding-bottom: 0px; diff --git a/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm b/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm index 8ca2b170f9954c376e3b7a5d9cde0f717b792a21..41babb23fcda36252325fb0af436a9f930ab5292 100755 --- a/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm +++ b/src/main/webapp/xnat-templates/navigations/XNATQuickSearch.vm @@ -1,35 +1,11 @@ <!-- search script --> <script type="text/javascript"> -<!-- -function DefaultEnterKey(e,button){ -var keynum, keychar, numcheck ; - -if(window.event) // IE - { - keynum = e.keyCode ; - if (keynum==13){ - submitQuickSearch(); - return true; - } - } -else if(e) // Netscape/Firefox/Opera - { - keynum = e.which ; - if (keynum==13){ - submitQuickSearch(); - return false; - } - } - return true; -} - function submitQuickSearch(){ - concealContent(); - if(document.getElementById('quickSearchForm').value!="") - document.getElementById('quickSearchForm').submit(); + concealContent(); + if($('#searchValue').val()!="") { + $('#quickSearchForm').submit(); + } } - -//--> </script> <!-- end search script --> @@ -64,15 +40,21 @@ function submitQuickSearch(){ <!-- stored searches will show up here --> </optgroup> </select> - <input id="searchValue" class="clean" name="searchValue" type="text" maxlength="40" size="20" value="$!field" /> + <input id="searchValue" class="clean" name="searchValue" type="text" maxlength="40" size="20" value="$!field"/> <button type="button" id="search_btn" class="btn2" onclick="submitQuickSearch();">Go</button> #if($turbineUtils.toBoolean($siteConfig.getProperty("UI.allow-advanced-search","true"))) <script> (function(){ - - $('#searchValue').each(function(){ + var searchField = $('#searchValue'); + searchField.keyup(function( event ) { + if (event.which == 13) { + submitQuickSearch(); + } + }); + + searchField.each(function(){ this.value = this.value || 'search'; $(this).focus(function(){ $(this).removeClass('clean'); diff --git a/src/main/webapp/xnat-templates/screens/XDATScreen_search_wizard2.vm b/src/main/webapp/xnat-templates/screens/XDATScreen_search_wizard2.vm index 8a41e47bbe4f027bc2de8005a5e7678597e7840b..64020385dc209c9a92e8d883b996ad4c21b25c92 100644 --- a/src/main/webapp/xnat-templates/screens/XDATScreen_search_wizard2.vm +++ b/src/main/webapp/xnat-templates/screens/XDATScreen_search_wizard2.vm @@ -57,13 +57,13 @@ <p>Define the constraints which will determine which rows show up in your search results.</p> - <p style="margin-bottom:20px;"> + <!-- p style="margin-bottom:20px;"> You must also select a search method (this affects all tabs below): <label class="search-method-label" style="border-right:1px solid #ccc;"> <input type="checkbox" class="search-method by-criteria" value="by-criteria"> By Specific Criteria</label> <label class="search-method-label"> <input type="checkbox" class="search-method by-id" value="by-id"> By Exact ID</label> - </p> + </p --> <div id="super_search" class="yui-navset" style="max-width:800px !important;margin-top:30px;margin-left:-40px;"> <ul class="yui-nav" style="">