From 65fda43d6d0e8a561af428f0802804d8a8805c3b Mon Sep 17 00:00:00 2001
From: Mike McKay <mfmckay@wustl.edu>
Date: Thu, 24 Mar 2016 16:19:54 -0500
Subject: [PATCH] XNAT-3791 Made changes to enable script versioning.

---
 .../org/nrg/xnat/restlet/XNATApplication.java |   3 +-
 .../restlet/resources/ScriptResource.java     |  57 +++--
 .../resources/ScriptVersionsResource.java     | 144 ++++++++++++
 .../webapp/scripts/xnat/app/scriptEditor.js   | 208 +++++++++++-------
 .../xnat-templates/screens/EditScript.vm      |   3 +
 .../webapp/xnat-templates/screens/Scripts.vm  |  14 +-
 6 files changed, 324 insertions(+), 105 deletions(-)
 create mode 100644 src/main/java/org/nrg/xnat/restlet/resources/ScriptVersionsResource.java

diff --git a/src/main/java/org/nrg/xnat/restlet/XNATApplication.java b/src/main/java/org/nrg/xnat/restlet/XNATApplication.java
index a78986a6..64cd2074 100755
--- a/src/main/java/org/nrg/xnat/restlet/XNATApplication.java
+++ b/src/main/java/org/nrg/xnat/restlet/XNATApplication.java
@@ -337,7 +337,8 @@ public class XNATApplication extends Application {
         attachURI(router, "/services/features", FeatureDefinitionRestlet.class);
 
         attachURIs(router, ScriptRunnerResource.class, "/automation/runners", "/automation/runners/{LANGUAGE}", "/automation/runners/{LANGUAGE}/{VERSION}");
-        attachURIs(router, ScriptResource.class, "/automation/scripts", "/automation/scripts/{SCRIPT_ID}");
+        attachURIs(router, ScriptResource.class, "/automation/scripts", "/automation/scripts/{SCRIPT_ID}", "/automation/scripts/{SCRIPT_ID}/{VERSION}");
+        attachURIs(router, ScriptVersionsResource.class, "/automation/scriptVersions", "/automation/scriptVersions/{SCRIPT_ID}");
         attachURIs(router, EventResource.class, "/automation/events", "/automation/events/{EVENT_ID}");
         attachURIs(router, WorkflowEventResource.class, "/automation/workflows", "/automation/workflows/{SPEC}");
         attachURIs(router, ScriptTriggerResource.class, "/automation/handlers",
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/ScriptResource.java b/src/main/java/org/nrg/xnat/restlet/resources/ScriptResource.java
index bea77c2f..6407ba30 100644
--- a/src/main/java/org/nrg/xnat/restlet/resources/ScriptResource.java
+++ b/src/main/java/org/nrg/xnat/restlet/resources/ScriptResource.java
@@ -44,6 +44,8 @@ public class ScriptResource extends AutomationResource {
 
         _scriptId = (String) getRequest().getAttributes().get(SCRIPT_ID);
 
+        _version = (String) getRequest().getAttributes().get(VERSION);
+
         // If the user isn't a site admin, there's a limited set of operations they are permitted to perform.
         if (!Roles.isSiteAdmin(user)) {
             // You can't put or post or delete a script and you can't retrieve a specific script OTHER THAN the split
@@ -91,21 +93,27 @@ public class ScriptResource extends AutomationResource {
 
         if (StringUtils.isNotBlank(_scriptId)) {
             try {
-                // They're requesting a specific script, so return that to them.
-                Script script = getScript();
-
-                // Here's a special case: if they're trying to get the split PET/MR script and it doesn't exist, give
-                // them the default implementation.
-                // TODO This should be expanded into a default script repository function.
-                if (script == null && _scriptId.equalsIgnoreCase(PrearcDatabase.SPLIT_PETMR_SESSION_ID)) {
-                    script = PrearcDatabase.DEFAULT_SPLIT_PETMR_SESSION_SCRIPT;
+                if (StringUtils.isNotBlank(_version)) {
+                    //They're requesting a specific version of a specific script
+                    return new StringRepresentation(MAPPER.writeValueAsString(_scriptService.getVersion(_scriptId, _version)), mediaType);
                 }
-
-                // have to check if it's null, or else it will return a StringRepresentation containing the word null instead of a 404
-                if (script != null) {
-                    return new StringRepresentation(MAPPER.writeValueAsString(script), mediaType);
-                } else {
-                    return null;
+                else {
+                    // They're requesting a specific script, so return that to them.
+                    Script script = getScript();
+
+                    // Here's a special case: if they're trying to get the split PET/MR script and it doesn't exist, give
+                    // them the default implementation.
+                    // TODO This should be expanded into a default script repository function.
+                    if (script == null && _scriptId.equalsIgnoreCase(PrearcDatabase.SPLIT_PETMR_SESSION_ID)) {
+                        script = PrearcDatabase.DEFAULT_SPLIT_PETMR_SESSION_SCRIPT;
+                    }
+
+                    // have to check if it's null, or else it will return a StringRepresentation containing the word null instead of a 404
+                    if (script != null) {
+                        return new StringRepresentation(MAPPER.writeValueAsString(script), mediaType);
+                    } else {
+                        return null;
+                    }
                 }
             } catch (IOException e) {
                 throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "An error occurred marshalling the script data to JSON", e);
@@ -158,6 +166,7 @@ public class ScriptResource extends AutomationResource {
         columns.add("Script ID");
         columns.add("Language");
         columns.add("Description");
+        //columns.add("Version");
 
         XFTTable table = new XFTTable();
         table.initTable(columns);
@@ -167,6 +176,7 @@ public class ScriptResource extends AutomationResource {
             table.insertRowItems(script.getScriptId(),
                     script.getLanguage(),
                     script.getDescription());
+                    //script.getScriptVersion());
         }
 
         return representTable(table, mediaType, params);
@@ -213,6 +223,23 @@ public class ScriptResource extends AutomationResource {
         if (properties.containsKey("scriptId")) {
             properties.remove("scriptId");
         }
+//        int previousMaxVersion = 0;
+//        try{
+//            int version = Integer.parseInt(_scriptService.getByScriptId(_scriptId).getScriptVersion());
+//            if(version>0){
+//                previousMaxVersion=version;
+//            }
+//        }
+//        catch(Exception e){
+//            _log.error("",e);
+//        }
+//        if (properties.containsKey("scriptVersion") && !properties.getProperty("scriptVersion").isEmpty()) {
+//            //properties.setProperty("scriptVersion", ""+(Integer.parseInt(properties.getProperty("scriptVersion"))+1));
+//            properties.setProperty("scriptVersion", ""+(Integer.parseInt(properties.getProperty("scriptVersion"))));
+//        }
+//        else{
+            //properties.setProperty("scriptVersion", ""+(previousMaxVersion+1));
+//        }
 
         try {
             _runnerService.setScript(_scriptId, properties);
@@ -225,9 +252,11 @@ public class ScriptResource extends AutomationResource {
     private static final Logger _log = LoggerFactory.getLogger(ScriptResource.class);
 
     private static final String SCRIPT_ID = "SCRIPT_ID";
+    private static final String VERSION = "VERSION";
     private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
 
     private final ScriptService _scriptService;
     private final ScriptRunnerService _runnerService;
     private final String _scriptId;
+    private final String _version;
 }
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/ScriptVersionsResource.java b/src/main/java/org/nrg/xnat/restlet/resources/ScriptVersionsResource.java
new file mode 100644
index 00000000..4d62c182
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/restlet/resources/ScriptVersionsResource.java
@@ -0,0 +1,144 @@
+package org.nrg.xnat.restlet.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.nrg.automation.entities.Script;
+import org.nrg.automation.services.ScriptRunnerService;
+import org.nrg.automation.services.ScriptService;
+import org.nrg.xdat.XDAT;
+import org.nrg.xdat.security.helpers.Roles;
+import org.nrg.xft.XFTTable;
+import org.nrg.xnat.helpers.prearchive.PrearcDatabase;
+import org.restlet.Context;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.StringRepresentation;
+import org.restlet.resource.Variant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+public class ScriptVersionsResource extends AutomationResource {
+
+    public ScriptVersionsResource(Context context, Request request, Response response) throws ResourceException {
+        super(context, request, response);
+
+        getVariants().add(new Variant(MediaType.APPLICATION_JSON));
+        getVariants().add(new Variant(MediaType.TEXT_HTML));
+        getVariants().add(new Variant(MediaType.TEXT_XML));
+        getVariants().add(new Variant(MediaType.TEXT_PLAIN));
+
+        _scriptService = XDAT.getContextService().getBean(ScriptService.class);
+        _runnerService = XDAT.getContextService().getBean(ScriptRunnerService.class);
+
+        _scriptId = (String) getRequest().getAttributes().get(SCRIPT_ID);
+
+        // If the user isn't a site admin, there's a limited set of operations they are permitted to perform.
+        if (!Roles.isSiteAdmin(user)) {
+            // You can't put or post or delete a script and you can't retrieve a specific script OTHER THAN the split
+            // PET/MR script, which is used by the upload applet.
+            if ((StringUtils.isNotBlank(_scriptId) && !_scriptId.equals(PrearcDatabase.SPLIT_PETMR_SESSION_ID))) {
+                _log.warn(getRequestContext("User " + user.getLogin() + " attempted to access forbidden script trigger template resources"));
+                response.setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Only site admins can view or update script resources.");
+                throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN, "Only site admins can view or update script resources.");
+            }
+        }
+
+        if (_log.isDebugEnabled()) {
+            _log.debug(getRequestContext("Servicing script request for user " + user.getLogin()));
+        }
+    }
+
+    @Override
+    protected String getResourceType() {
+        return Script.class.getSimpleName();
+    }
+
+    @Override
+    protected String getResourceId() {
+        return _scriptId;
+    }
+
+    @Override
+    public Representation represent(Variant variant) throws ResourceException {
+        final MediaType mediaType = overrideVariant(variant);
+
+        if (StringUtils.isNotBlank(_scriptId)) {
+            try {
+                List<String> versions = _scriptService.getVersions(_scriptId);
+
+//                // They're requesting a specific script, so return that to them.
+//                List<Script> script = getScripts();
+//
+//                // Here's a special case: if they're trying to get the split PET/MR script and it doesn't exist, give
+//                // them the default implementation.
+//                // TODO This should be expanded into a default script repository function.
+//                if (script == null && _scriptId.equalsIgnoreCase(PrearcDatabase.SPLIT_PETMR_SESSION_ID)) {
+//                    script = new ArrayList<Script>();
+//                    script.add(PrearcDatabase.DEFAULT_SPLIT_PETMR_SESSION_SCRIPT);
+//                }
+
+                // have to check if it's null, or else it will return a StringRepresentation containing the word null instead of a 404
+                if (versions != null) {
+                    return new StringRepresentation(MAPPER.writeValueAsString(versions), mediaType);
+                } else {
+                    return null;
+                }
+            } catch (IOException e) {
+                throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "An error occurred marshalling the script data to JSON", e);
+            }
+        } else {
+            // They're asking for list of available scripts, so give them that.
+            return listScripts(mediaType);
+        }
+    }
+
+    /**
+     * Lists the scripts at the specified scope and entity ID.
+     *
+     * @return A representation of the scripts available at the specified scope and entity ID (if specified).
+     */
+    private Representation listScripts(final MediaType mediaType) {
+        Hashtable<String, Object> params = new Hashtable<>();
+
+        ArrayList<String> columns = new ArrayList<>();
+        columns.add("Script ID");
+        columns.add("Language");
+        columns.add("Description");
+        columns.add("Version");
+
+        XFTTable table = new XFTTable();
+        table.initTable(columns);
+
+        final List<Script> scripts = _scriptService.getAll();
+        for (final Script script : scripts) {
+            table.insertRowItems(script.getScriptId(),
+                    script.getLanguage(),
+                    script.getDescription());
+//                    script.getScriptVersion());
+        }
+
+        return representTable(table, mediaType, params);
+    }
+
+    private List<Script> getScripts() {
+        return _runnerService.getScripts(_scriptId);
+    }
+
+    private static final Logger _log = LoggerFactory.getLogger(ScriptVersionsResource.class);
+
+    private static final String SCRIPT_ID = "SCRIPT_ID";
+    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+    private final ScriptService _scriptService;
+    private final ScriptRunnerService _runnerService;
+    private final String _scriptId;
+}
diff --git a/src/main/webapp/scripts/xnat/app/scriptEditor.js b/src/main/webapp/scripts/xnat/app/scriptEditor.js
index e18c92d8..2de23f98 100644
--- a/src/main/webapp/scripts/xnat/app/scriptEditor.js
+++ b/src/main/webapp/scripts/xnat/app/scriptEditor.js
@@ -45,6 +45,11 @@ var XNAT = getObject(XNAT||{});
         return XNAT.url.restUrl('/data/automation/scripts/' + scriptId, params);
     }
 
+    // return script url with common parts pre-defined
+    function scriptVersionsURL( scriptId, params ){
+        return XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId, params);
+    }
+
     function langMenuOptions(langs){
 
         langs = langs || scriptEditor.languages;
@@ -81,8 +86,8 @@ var XNAT = getObject(XNAT||{});
                     scriptEditor.scriptIds.push(script['Script ID']);
                     list +=
                         rowTemplate.
-                            replace(/__SCRIPT_ID__/g, script['Script ID']).
-                            replace(/__SCRIPT_DESCRIPTION__/g, XNAT.utils.escapeXML(script['Description']));
+                        replace(/__SCRIPT_ID__/g, script['Script ID']).
+                        replace(/__SCRIPT_DESCRIPTION__/g, XNAT.utils.escapeXML(script['Description']));
                 });
                 scriptsTable.find('> tbody').html(list);
                 scriptsTable.show();
@@ -138,6 +143,7 @@ var XNAT = getObject(XNAT||{});
             var data = {
                 content: ace.edit(editor_id).getSession().getValue(),
                 description: $dialog.find('.script-description').val(),
+                scriptVersion: $dialog.find('.script-version').val(),
                 language: $dialog.find('input.language').val() || ''
             };
 
@@ -167,57 +173,18 @@ var XNAT = getObject(XNAT||{});
     }
 
     var counter = 0;
-
-    // load script into the editor
-    function loadEditor($dialog, json){
-
-        json = json || {};
-
-        console.log(json);
-
-        var scriptId = json.scriptId || '';
-        var lang = json.language || 'groovy';
-        var time = json.timestamp || '';
-
-        $dialog.find('.id').val(json.id || '');
-        $dialog.find('.scriptId').val(scriptId);
-        $dialog.find('.language').val(lang);
-        $dialog.find('.timestamp').val(time);
-        $dialog.find('.script-description').val(json.description || '');
-
-        if (scriptId){
-            $dialog.find('.script-id-text').html(scriptId);
-            $dialog.find('.script-id-input').remove();
-            //$dialog.find('.script-id-input').val(scriptId);
-        }
-
-        var $wrapper = $dialog.find('.editor-wrapper');
-
-        // make sure the editor wrapper is empty
-        $wrapper.empty();
-
-        // create an entirely new editor div
-        var _editor = document.createElement('div');
-        _editor.id = 'script-' + (scriptId || (json.id||++counter)) + '-content';
-        _editor.className = 'editor-content';
-        _editor.innerHTML = XNAT.utils.escapeXML(json.content) || '';
-        _editor.style = 'position:absolute;top:0;right:0;bottom:0;left:0;border: 1px solid #ccc';
-
-        // put the new editor div in the wrapper
-        $wrapper.append(_editor);
-
-        // save the id to outer scope for other functions
-        scriptEditor.editor_id = _editor.id;
-
-        var aceEditor = ace.edit(_editor);
-        aceEditor.setTheme("ace/theme/eclipse");
-        aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
-
-    }
-
-
     // open xmodal dialog for script editing
     function renderEditor( json ){
+        //var fullJson = json || {};
+        //json = {};
+        //var largestVersion = -1;
+        //var arrayLength = fullJson.length;
+        //for (var i = 0; i < arrayLength; i++) {
+        //    if(fullJson[i].scriptVersion>largestVersion){
+        //        largestVersion = fullJson[i].scriptVersion;
+        //        json = fullJson[i];
+        //    }
+        //}
 
         var scriptId = json.scriptId || '';
         var lang = json.language || 'groovy';
@@ -236,7 +203,7 @@ var XNAT = getObject(XNAT||{});
         opts.footerContent = '<span style="color:#555;">';
         if (time){
             opts.footerContent +=
-            'last modified: ' + (new Date(time)).toString();
+                'last modified: ' + (new Date(time)).toString();
         }
         opts.footerContent += '</span>';
         opts.buttons = {
@@ -259,51 +226,123 @@ var XNAT = getObject(XNAT||{});
 
             var $dialog = obj.$modal;
 
-            loadEditor($dialog, json);
+            $dialog.find('.id').val(json.id || '');
+            $dialog.find('.scriptId').val(scriptId);
+            $dialog.find('.language').val(lang);
+            $dialog.find('.timestamp').val(time);
+            $dialog.find('.script-description').val(json.description || '');
+
+            var currScriptVersion = 1;
+            if(scriptId) {
+                //xhr.getJSON(XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId), function (jsonVersions) {
+                //    for(var key in jsonVersions){
+                //        if(key!="contains") {
+                //            var currScript = jsonVersions[key];
+                //            currScriptVersion = currScript.scriptVersion;
+                //            $('#script-version')
+                //                .append($("<option></option>")
+                //                    .attr("value", currScriptVersion)
+                //                    .text(currScriptVersion));
+                //        }
+                //    }
+                //    $('#script-version').val(currScriptVersion);
+                //});
+                xhr.getJSON(XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId), function (versionsList) {
+                    var versCounter = 1;
+                    for(var vers in versionsList){
+                        if(vers!="contains") {
+                            currScriptVersion = versionsList[vers];
+                            $('#script-version')
+                                .append($("<option></option>")
+                                    .attr("value", currScriptVersion)
+                                    .text(versCounter));
+                            versCounter = versCounter+1;
+                        }
+                    }
+                    $('#script-version').val(currScriptVersion);
+                });
+            }
+            else{
+                $('#script-version')
+                    .append($("<option></option>")
+                        .attr("value", "1")
+                        .attr("selected","selected")
+                        .text("1"));
+            }
+            //$("div.script-version select").val(currScriptVersion);
+            $('#script-version').change(function(){
+                var selectedVersion = $('#script-version')[0].value;
+                xhr.getJSON(XNAT.url.restUrl('/data/automation/scripts/' + scriptId + '/'+ selectedVersion), function (versionObj) {
+                    json = versionObj;
+                    var $dialog = obj.$modal;
+
+                    //$dialog.find('.id').val(json.id || '');
+                    $dialog.find('.scriptId').val(scriptId || '');
+                    $dialog.find('.language').val(json.language || '');
+                    $dialog.find('.timestamp').val(json.timestamp || '');
+                    $dialog.find('.script-description').val(json.description || '');
+                    $dialog.find('.script-version').val(selectedVersion || '');
+
+                    var $wrapper = $dialog.find('.editor-wrapper');
+
+                    // make sure the editor wrapper is empty
+                    $wrapper.empty();
+
+                    // create an entirely new editor div
+                    var _editor = document.createElement('div');
+                    _editor.id = 'script-' + (scriptId || (json.id||++counter)) + '-content';
+                    _editor.className = 'editor-content';
+                    _editor.innerHTML = XNAT.utils.escapeXML(json.content) || '';
+                    _editor.style = 'position:absolute;top:0;right:0;bottom:0;left:0;border: 1px solid #ccc';
+
+                    // put the new editor div in the wrapper
+                    $wrapper.append(_editor);
+
+                    // save the id to outer scope for other functions
+                    scriptEditor.editor_id = _editor.id;
+
+                    var aceEditor = ace.edit(_editor);
+                    aceEditor.setTheme("ace/theme/eclipse");
+                    aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
+                });
+
+            });
 
-            /*
+            if (scriptId){
+                $dialog.find('.script-id-text').html(scriptId);
+                $dialog.find('.script-id-input').remove();
+                //$dialog.find('.script-id-input').val(scriptId);
+            }
 
-            // VERSION MENU START
-            // NEEDS EDITING FOR IMPLEMENTATION
+            var $wrapper = $dialog.find('.editor-wrapper');
 
-            var $versionMenu = $dialog.find('.script-version');
+            // make sure the editor wrapper is empty
+            $wrapper.empty();
 
-            //////////////////////////////////////////////////////////////////////
-            // populate the 'version' menu
-            // THIS IS A PLACEHOLDER
-            // REPLACE WITH VERSIONS FROM 'json'
-            //////////////////////////////////////////////////////////////////////
-            $('#scripts-table').find('[data-script-id]').each(function(){
-                var id = $(this).data('script-id');
-                var $option = $(document.createElement('option'));
-                //$option.attr('data-url', scriptURL(id));
-                $option.html(id);
-                $option.val(id);
-                $versionMenu.append($option);
-            });
-            //////////////////////////////////////////////////////////////////////
+            // create an entirely new editor div
+            var _editor = document.createElement('div');
+            _editor.id = 'script-' + (scriptId || (json.id||++counter)) + '-content';
+            _editor.className = 'editor-content';
+            _editor.innerHTML = XNAT.utils.escapeXML(json.content) || '';
+            _editor.style = 'position:absolute;top:0;right:0;bottom:0;left:0;border: 1px solid #ccc';
 
-            // put select menu handler here
-            $versionMenu.on('change', function(){
-                xhr.getJSON(scriptURL(this.value), function(data){
-                    loadEditor($dialog, data)
-                });
-            });
+            // put the new editor div in the wrapper
+            $wrapper.append(_editor);
 
-            // VERSION MENU END
+            // save the id to outer scope for other functions
+            scriptEditor.editor_id = _editor.id;
 
-            */
+            var aceEditor = ace.edit(_editor);
+            aceEditor.setTheme("ace/theme/eclipse");
+            aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
 
         };
         opts.afterShow = function(obj){
             if (!scriptId){
                 obj.$modal.find('.script-id-input').focus().select();
             }
-            // TODO: prompt user to save if they could lose unsaved changes
         };
-
         xmodal.open(opts);
-
     }
 
     // open dialog to choose language
@@ -402,8 +441,11 @@ var XNAT = getObject(XNAT||{});
                 xmodal.closeAll();
             }
         };
+        var csrfParam = {
+            XNAT_CSRF: csrfToken
+        };
 
-        xhr.delete(scriptURL(scriptId), {
+        xhr.delete(scriptURL(scriptId, csrfParam), {
             success: function(){
                 xmodal.message(successDialog);
             },
diff --git a/src/main/webapp/xnat-templates/screens/EditScript.vm b/src/main/webapp/xnat-templates/screens/EditScript.vm
index 161e0523..57b4fc97 100755
--- a/src/main/webapp/xnat-templates/screens/EditScript.vm
+++ b/src/main/webapp/xnat-templates/screens/EditScript.vm
@@ -67,6 +67,9 @@
                 <tr>
                     <td><label for="scriptId"><strong>Script ID:</strong></label></td><td><input type="text" name="scriptId" id="scriptId" value="$!script.scriptId"/></td>
                 </tr>
+                <tr>
+                    <td><label for="scriptVersion"><strong>Version:</strong></label></td><td><input type="text" name="scriptVersion" id="scriptVersion" size="80" value="$!script.scriptVersion" readonly/></td>
+                </tr>
                 <tr>
                     <td><label for="description"><strong>Description:</strong></label></td><td><input type="text" name="description" id="description" size="80" value="$!script.description"/></td>
                 </tr>
diff --git a/src/main/webapp/xnat-templates/screens/Scripts.vm b/src/main/webapp/xnat-templates/screens/Scripts.vm
index db72d0dc..9186484e 100644
--- a/src/main/webapp/xnat-templates/screens/Scripts.vm
+++ b/src/main/webapp/xnat-templates/screens/Scripts.vm
@@ -27,7 +27,7 @@
 
 <div class="yui-skin-sam">
 
-    <div id="tp_fm" style="display:none"></div>
+##    <div id="tp_fm" style="display:none"></div>
 
     #if($data.getSession().getAttribute("user").checkRole("Administrator"))
 
@@ -133,7 +133,7 @@
                             </table>
                             <br>
                             <b style="padding:0 8px;">Create a site-wide Event Handler: </b>
-                            <button type="button" id="manage_event_handlers" class="btn1" style="font-size:12px;" title="manage event handlers">Manage Event Handlers</button>
+    <button type="button" id="add_event_handler" class="btn1" style="font-size:12px;" title="add an event handler">Add Event Handler</button>
                         </div>
 
 
@@ -184,7 +184,7 @@
                                 <thead>
                                 <th>Script ID</th>
                                 <th width="50%">Description</th>
-                                ##                <th>Version</th>
+                                ##<th>Version</th>
                                 <th>&nbsp;</th>
                                 ##                <th>&nbsp;</th>
                                 ##                <th>&nbsp;</th>
@@ -314,12 +314,12 @@
                     <td><b>Description: </b>&nbsp;</td>
                     <td><input type="text" name="script-description" class="script-description" size="80" value=""></td>
                 </tr>
-                <!-- VERSION MENU
                 <tr>
-                    <td><b>Load Version: </b>&nbsp;</td>
-                    <td><select name="script-version" class="script-version"></select></td>
+                    <td><b>Script Version: </b>&nbsp;</td>
+                    <td><select id="script-version" class="script-version">
+                        <option value="!">Select a Version</option>
+                    </select></td>
                 </tr>
-                -->
             </table>
             <br>
             <div class="editor-wrapper" style="width:840px;height:482px;position:relative;">
-- 
GitLab