source: trunk/www/plugins/net/sf/basedb/plugins/executor/external_plugin_parameters.jsp @ 5956

Last change on this file since 5956 was 5956, checked in by Nicklas Nordborg, 10 years ago

References #1655: GUI improvements

  • External program executor configuration dialog.
  • Exception dialogs.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 21.4 KB
Line 
1<%-- $Id: external_plugin_parameters.jsp 5956 2012-02-13 15:27:40Z nicklas $
2  ------------------------------------------------------------------
3  Copyright (C) 2010 Martin Svensson
4 
5  This file is part of BASE - BioArray Software Environment.
6  Available at http://base.thep.lu.se/
7
8  BASE is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License
10  as published by the Free Software Foundation; either version 3
11  of the License, or (at your option) any later version.
12
13  BASE is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with BASE. If not, see <http://www.gnu.org/licenses/>.
20  ------------------------------------------------------------------
21
22  @author Martin
23  @version 2.15
24--%>
25<%@ page pageEncoding="UTF-8" session="false"
26  import="net.sf.basedb.core.DbControl"
27  import="net.sf.basedb.core.Item"
28  import="net.sf.basedb.core.ItemContext"
29  import="net.sf.basedb.core.ParameterType"
30  import="net.sf.basedb.core.PluginDefinition"
31  import="net.sf.basedb.core.PluginConfiguration"
32  import="net.sf.basedb.core.PluginConfigurationRequest"
33  import="net.sf.basedb.core.PluginParameter" 
34  import="net.sf.basedb.core.RequestInformation"
35  import="net.sf.basedb.core.SessionControl"
36  import="net.sf.basedb.core.StringParameterType"
37  import="net.sf.basedb.util.error.ThrowableUtil" 
38  import="net.sf.basedb.clients.web.Base"
39  import="net.sf.basedb.clients.web.WebException"
40  import="net.sf.basedb.clients.web.util.HTML" 
41  import="net.sf.basedb.plugins.executor.ExternalProgramExecutor"
42  import="net.sf.basedb.plugins.executor.ParameterDefinition"
43  import="net.sf.basedb.util.Values" 
44  import="java.util.ArrayList"
45  import="java.util.Arrays"
46  import="java.util.Collections"
47  import="java.util.HashMap"
48  import="java.util.Iterator"
49  import="java.util.List"
50  import="java.util.Map" 
51  import="org.jdom.Element"
52%>
53<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
54<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
55<%
56  final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
57final String ID = sc.getId();
58final float scale = Base.getScale(sc);
59final String clazz = "class=\"text\"";
60final String requiredClazz = "class=\"text required\"";
61
62DbControl dc = null;
63try
64{
65  PluginDefinition plugin = (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin"); 
66  PluginConfiguration pluginConfig = (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
67  PluginConfigurationRequest pcRequest = (PluginConfigurationRequest)sc.getSessionSetting("plugin.configure.request");
68  RequestInformation ri = pcRequest.getRequestInformation();
69  String title = HTML.encodeTags(ri.getTitle());
70  String helpText = ri.getDescription(); 
71  if (helpText == null && config != null) helpText = pluginConfig.getDescription();
72  if (helpText == null) helpText = plugin.getDescription();
73
74  String errorMessage = (String)sc.getSessionSetting("plugin.configure.errors.message");
75  List<Throwable> errors = (List<Throwable>)sc.getSessionSetting("plugin.configure.errors.list");
76 
77  String xml = request.getParameter("parameter:externalParameters");
78  if (xml == null) xml = (String)pcRequest.getCurrentParameterValue("externalParameters");
79  List<ParameterDefinition> externalParameters = null;
80  try
81  {
82    externalParameters = ParameterDefinition.parseXml(xml, false);
83    if (true) throw new RuntimeException("fff");
84  }
85  catch (Throwable ex)
86  {
87    errorMessage = "Could not load parameter definitions";
88    errors = Collections.singletonList(ex);
89  }
90  %>
91  <base:page type="popup" title="<%=title%>">
92  <base:head scripts="plugin.js,xml.js" styles="parameters.css,toolbar.css">
93    <script language="JavaScript">
94      function init()
95      {
96        var frm = document.forms['configure'];
97        var pd;
98        <%
99        if (externalParameters != null)
100        {
101          for (ParameterDefinition pdef : externalParameters)
102          {
103            %>
104            pd = new ParameterDefinition('<%=pdef.getType()%>', '<%=HTML.javaScriptEncode(pdef.getName())%>', <%=pdef.isRequired() ? 1 : 0%>);
105            pd.multiple = <%=pdef.isMultiple() ? "1" : "0"%>;
106            pd.defaultValue = '<%=HTML.javaScriptEncode(pdef.getDefaultValue())%>';
107            pd.description = '<%=HTML.javaScriptEncode(pdef.getDescription())%>';
108            <%
109            for (String option : pdef.getOptions())
110            {
111              %>
112              pd.setOption('<%=option%>', '<%=HTML.javaScriptEncode(pdef.getOption(option))%>');
113              <%
114            }
115            %>
116            allParameters[allParameters.length] = pd;
117            pd.addToList(frm.programParameters);
118            <%
119          }
120        }
121        %>
122      }
123
124      function hideErrorList()
125      {
126        Main.hide('errorlist');
127        Main.show('showerrorlist');
128      }
129      function showErrorList()
130      {
131        Main.show('errorlist');
132        Main.hide('showerrorlist');
133      }
134      function toggleStacktrace(index)
135      {
136        Main.showHide('stacktrace.' + index);
137        var img = document.getElementById('stacktracelink.'+index);
138        if (img.src.indexOf('show_section') > 0)
139        {
140          img.src = getRoot() + '/images/hide_section.png';
141        }
142        else
143        {
144          img.src = getRoot() + '/images/show_section.png';
145        }
146      }
147
148      function doCancel()
149      {
150        location = 'index.jsp?ID=<%=ID%>&cmd=CancelWizard';
151      }
152
153      function saveSettings()
154      {
155        var frm = document.forms['configure'];
156        var xml = '<parameter-definition>\n';
157        for (var i=0; i < allParameters.length; i++)
158        {
159          var pdef = allParameters[i];
160          var error = pdef.validate(frm);
161          if (error)
162          {
163            frm.programParameters.selectedIndex = i;
164            parameterOnClick();
165            if (error.field.focus) error.field.focus();
166            alert(error.msg);
167            return;
168          }
169          xml += pdef.toXml() + '\n';
170        }
171        xml += '</parameter-definition>\n';
172        frm['parameter:externalParameters'].value = xml;
173        frm.submit();
174      }
175
176      function setRadio(name, index)
177      {
178        var frm = document.forms['configure'];
179        frm[name][index].click();
180      }
181
182      function parameterTypeOnChange()
183      {
184        updateForm();
185        onFormChange();
186      }
187     
188      function onFormChange()
189      {
190        var frm = document.forms['configure'];
191        var parameterDef = allParameters[currentParameter];
192        parameterDef.readFromForm(frm);
193        parameterDef.updateListOption(frm.programParameters[currentParameter]);
194      }
195
196      function addOnClick()
197      {
198        var frm = document.forms['configure'];
199        var pdef = new ParameterDefinition('string', 'new', 0);
200        allParameters[allParameters.length] = pdef;
201        pdef.addToList(frm.programParameters);
202        frm.programParameters.selectedIndex = frm.programParameters.length-1;
203        parameterOnClick();
204        frm.parameterName.focus();
205        frm.parameterName.select();
206      }
207
208
209      function removeOnClick()
210      {
211        var frm = document.forms['configure'];
212        var selected = frm.programParameters.selectedIndex;
213        if (!confirm('Remove ' + allParameters[selected].name + '?')) return;
214        frm.programParameters[selected] = null;
215        allParameters.splice(selected, 1);
216        Main.hide('parameterDefinition');
217      }
218     
219      function moveParameter(up)
220      {
221        var frm = document.forms['configure'];
222        var selected = frm.programParameters.selectedIndex;
223        Forms.moveListOptions(frm.programParameters, up);
224        var moveTo = -1;
225        if (up)
226        {
227          if (selected < allParameters.length - 1) moveTo = selected + 1;
228        }
229        else 
230        {
231          if (selected > 0) moveTo = selected - 1;
232        }
233        if (moveTo >= 0)
234        {
235          var pp = allParameters[selected];
236          allParameters[selected] = allParameters[moveTo];
237          allParameters[moveTo] = pp;
238        }
239      }
240
241      var currentParameter;
242      function parameterOnClick()
243      {
244        var frm = document.forms['configure'];
245        if (frm.programParameters.selectedIndex < 0) return;
246        currentParameter = frm.programParameters.selectedIndex;
247        var parameterDef = allParameters[currentParameter];
248        if (!parameterDef) return;
249        parameterDef.writeToForm(frm);
250        updateForm();
251      }
252
253      function updateForm()
254      {
255        var frm = document.forms['configure'];
256        var type = frm.parameterType[frm.parameterType.selectedIndex].value;
257        if (type == 'integer')
258        {
259          frm.parameterDefault.setAttribute("onkeypress", "return Numbers.integerOnly(event)");
260          frm.minValue.setAttribute("onkeypress", "return Numbers.integerOnly(event)");
261          frm.maxValue.setAttribute("onkeypress", "return Numbers.integerOnly(event)");
262          Main.show('minMax');
263        }
264        else if (type == 'float')
265        {
266          frm.parameterDefault.setAttribute("onkeypress", "return Numbers.numberOnly(event)");
267          frm.minValue.setAttribute("onkeypress", "return Numbers.numberOnly(event)");
268          frm.maxValue.setAttribute("onkeypress", "return Numbers.numberOnly(event)");
269          Main.show('minMax');
270        }
271        else
272        {
273          frm.parameterDefault.setAttribute("onkeypress", null);
274          Main.hide('minMax');
275        }
276        if (type == 'boolean')
277        {
278          Main.show('booleanDefault');
279          Main.hide('regularDefault');
280        }
281        else
282        {
283          Main.hide('booleanDefault');
284          Main.show('regularDefault');
285        }
286        if (type == 'enumeration')
287        {
288          Main.show('enumerationOptions');
289        }
290        else
291        {
292          Main.hide('enumerationOptions');
293        }
294       
295      }
296
297     
298      var allParameters = new Array();
299      function ParameterDefinition(parameterType, name, required)
300      {
301        this.parameterType = parameterType;
302        this.name = name;
303        this.required = required;
304        this.multiple = 0;
305        this.defaultValue = '';
306        this.description = '';
307        this.options = new Array();
308
309        this.setOption = function(key, value)
310        {
311          this.options[key] = value;
312        }
313
314        this.getOption = function(key)
315        {
316          return this.options[key];
317        }
318       
319        this.writeToForm = function(frm)
320        {
321          Forms.selectListOption(frm.parameterType, this.parameterType);
322          frm.parameterName.value = this.name;
323          frm.parameterIsRequired.checked = this.required;
324          frm.parameterIsMultiple.checked = this.multiple;
325          if (this.parameterType == 'boolean')
326          {
327            Forms.checkRadio(frm.booleanDefault, this.defaultValue);
328          }
329          else
330          {
331            frm.parameterDefault.value = this.defaultValue;
332          }
333          frm.enumerationOptions.value = this.options['enum'] || '';
334          frm.minValue.value = this.options['min'] || '';
335          frm.maxValue.value = this.options['max'] || '';
336          frm.parameterDescription.value = this.description;
337          if (frm.parameterXML) frm.parameterXML.value = this.toXml();
338        }
339
340        this.readFromForm = function(frm)
341        {
342          this.parameterType = frm.parameterType[frm.parameterType.selectedIndex].value;
343          this.name = frm.parameterName.value;
344          this.required = frm.parameterIsRequired.checked ? 1 : 0;
345          this.multiple = frm.parameterIsMultiple.checked ? 1 : 0;
346          if (this.parameterType == 'boolean')
347          {
348            var radio = Forms.getCheckedRadio(frm.booleanDefault);
349            this.defaultValue = radio ? radio.value : '';
350          }
351          else
352          {
353            this.defaultValue = frm.parameterDefault.value;
354          }
355          if (this.parameterType == 'enumeration')
356          {
357            this.setOption('enum', frm.enumerationOptions.value);
358          }
359          if (this.parameterType == 'integer' || this.parameterType == 'float')
360          {
361            this.setOption('min', frm.minValue.value);
362            this.setOption('max', frm.maxValue.value);
363          }
364          this.description = frm.parameterDescription.value;
365        }
366
367        this.updateListOption = function(listOption)
368        {
369          listOption.value = this.name;
370          listOption.text = this.toString();
371        }
372       
373        this.addToList = function(list)
374        {
375          Forms.addListOption(list, -1, new Option(this.toString(), this.name));
376        }
377       
378        this.toString = function()
379        {
380          return this.name + ' (' + this.parameterType + ')';
381        }
382
383        this.toXml = function()
384        {
385          var doc = Xml.createDocument('parameter');
386          var root = doc.firstChild;
387          Xml.setAttribute(root, 'type', this.parameterType);
388          Xml.setAttribute(root, 'name', this.name);
389          Xml.setAttribute(root, 'required', this.required ? 'true' : 'false');
390          Xml.setAttribute(root, 'multiple', this.multiple ? 'true' : 'false');
391          if (this.description) Xml.addTag(root, 'description', this.description);
392          if (this.defaultValue) Xml.addTag(root, 'default', this.defaultValue);
393          var optionsTag;
394          for (var option in this.options)
395          {
396            if (!optionsTag) optionsTag = Xml.addTag(root, 'options');
397            var optionTag = Xml.addTag(optionsTag, 'option', this.options[option]);
398            Xml.setAttribute(optionTag, 'key', option);
399          }
400          return Xml.toString(doc);
401        }
402
403        this.validate = function(frm)
404        {
405          // A parameter name is required
406          if (Main.trimString(this.name) == '')
407          {
408            return new ValidationError('Missing name for parameter', frm.parameterName);
409          }
410          // A parameter type is required
411          else if (Main.trimString(this.parameterType) == '')
412          {
413            return new ValidationError('Missing type for parameter', frm.parameterType);
414          }
415          // The name must be unique
416          for (var i = 0; i < allParameters.length; i++)
417          {
418            var other = allParameters[i];
419            if (other == this) break;
420            if (other.name == this.name)
421            {
422              return new ValidationError('Parameter has duplicate name: ' + this.name, 
423                  frm.parameterName);
424            }
425          }
426          if (this.parameterType == 'enumeration')
427          {
428            // An enumeration must have at least one option
429            var options = Main.trimString(this.getOption('enum'));
430            if (options == '')
431            {
432              return new ValidationError('No options for enumeration: ' + this.name, 
433                  frm.enumerationOptions);
434            }
435            // If a default value is specified it must be among the options
436            else if (this.defaultValue)
437            {
438              var opt = options.split(/[\n\r]/);
439              var index = -1;
440              for (var i = 0; i < opt.length; i++)
441              {
442                if (opt[i] == this.defaultValue)
443                {
444                  index = i;
445                  break;
446                }
447              }
448              if (index == -1)
449              {
450                return new ValidationError("'" + this.defaultValue + "' is not an option in enumeration: " + this.name, 
451                    frm.parameterDefault);
452              }
453            }
454          }
455          else if (this.parameterType == 'integer' || this.parameterType == 'float')
456          {
457            // If a default value is specified, it must be within max and min limits
458            if (this.defaultValue)
459            {
460              var def = parseFloat(this.defaultValue);
461              var min = parseFloat(this.getOption('min'));
462              var max = parseFloat(this.getOption('max'));
463              if (def < min)
464              {
465                return new ValidationError(def + ' < ' + min + ' for parameter: ' + this.name, 
466                    frm.parameterDefault);
467              }
468              else if (def > max)
469              {
470                return new ValidationError(def + ' > ' + max + ' for parameter: ' + this.name,
471                    frm.parameterDefault);
472              }
473            }
474          }
475          return null;
476        }
477      }
478
479      function ValidationError(msg, field)
480      {
481        this.msg = msg;
482        this.field = field;
483      }
484     
485    </script>
486  </base:head>
487  <base:body onload="init()">
488    <h1><%=title%> <base:help helpid="executor.program.parameters" /></h1>
489
490    <form action="index.jsp" method="post" name="configure" onsubmit="return false;">
491    <input type="hidden" name="ID" value="<%=ID%>">
492    <input type="hidden" name="cmd" value="SetParameters">
493    <input type="hidden" name="requestId" value="<%=request.getParameter("requestId")%>">
494    <input type="hidden" name="parameter:externalParameters" value="">
495   
496    <div class="content">
497   
498      <div class="absolutefull filled" style="height: 4.5em;">
499        <table style="height: 100%; margin:auto;"><tr><td style="padding: 3px;">
500        <b>
501          <%=plugin == null ? "" : HTML.encodeTags(plugin.getName())%>
502          <%=pluginConfig == null ? "" : "(" + HTML.encodeTags(pluginConfig.getName()) + ")"%>
503        </b><br>
504        <%=HTML.niceFormat(helpText)%>
505        </td></tr></table>
506      </div>
507   
508      <div class="absolutefull topborder bottomborder" style="top: 4.5em;">
509      <div class="absolutefull filled" style="width: 18em;">
510
511          <tbl:toolbar subclass="bottomborder">
512            <tbl:button 
513              image="add.png" 
514              title="Add" 
515              onclick="addOnClick()"
516              tooltip="Add a new parameter definition"
517            />
518            <tbl:button 
519              image="remove.png" 
520              title="Remove" 
521              onclick="removeOnClick()"
522              tooltip="Remove the selected parameter(s)"
523            />
524            <tbl:button 
525              image="move_up.png"
526              onclick="moveParameter(false)" 
527              tooltip="Move the selected paramer(s) up"
528            />
529            <tbl:button 
530              image="move_down.png" 
531              onclick="moveParameter(true)" 
532              tooltip="Move the selected paramer(s) down"
533            />
534          </tbl:toolbar>
535         
536          <select name="programParameters" size="20" style="width: 99%;" onclick="parameterOnClick()"></select>
537      </div>
538     
539      <div class="absolutefull leftborder" style="left: 18em;">
540        <table id="parameterDefinition" class="fullform input100" style="xdisplay: none;">
541        <%
542        if (errorMessage != null || (errors != null && errors.size() > 0))
543        {
544          %>
545          <tr>
546            <td colspan="2">
547            <div id="errors">
548              <div class="messagecontainer error">
549              <%=errorMessage %>
550             
551              <%
552              if (errors != null && errors.size() > 0)
553              {
554                %>
555                <div id="showerrorlist">
556                  <base:icon image="gonext.png" 
557                    onclick="showErrorList()" 
558                    style="color: #FFFFFF;"
559                    tooltip="Show more information about each error"
560                  />
561                </div>
562                <div id="errorlist" style="display: none; margin: 0px;">
563                  <base:icon image="move_down.png" 
564                    onclick="hideErrorList()" 
565                    style="color: #FFFFFF;" 
566                    tooltip="Show less information"
567                  />
568                <ol>
569                <%
570                int i = 0;
571                for (Throwable t : errors)
572                {
573                  ++i;
574                  %>
575                  <li><%=t.getMessage()%>
576                    <base:icon 
577                      image="gonext.png" 
578                      onclick="<%="toggleStacktrace(" + i + ")"%>" 
579                      tooltip="Toggle display of detailed stacktrace"
580                      id="<%="stacktracelink." + i %>"
581                    />
582                    <div id="stacktrace.<%=i%>" class="stacktrace" 
583                      style="display:none; height: 15em;"><%=ThrowableUtil.stackTraceToString(t)%></div>
584                    <%
585                  }
586                  %>
587                </ol>
588                </div>
589                <%
590              }
591              %>
592              </div>
593            </div>
594            </td>
595          </tr>
596          <%
597        }
598        %>
599        <tr>
600          <th>Name</th>
601          <td><input type="text" <%=requiredClazz%> name="parameterName" onchange="onFormChange()"></td>
602        </tr>
603        <tr>
604          <th>Parameter type</th>
605          <td>
606            <select name="parameterType" size="1" onClick="parameterTypeOnChange()" class="required">
607              <option value="string" 
608                title="A free-text string no more than 255 characters"
609                >String (short)</option>
610              <option value="text"
611                title="A free-text string up to 64K characters"
612                >String (long)</option>
613              <option value="integer"
614                title="An integer that is optionally bounded"
615                >Integer</option>
616              <option value="float"
617                title="A floating point number"
618                >Float</option>
619              <option value="boolean"
620                title="A true/false selection"
621                >Boolean</option>
622              <option value="enumeration"
623                title="Select value(s) from a predefined list"
624                >Enumeration</option>
625              <option value="factor"
626                title="Select experimental factor(s) to include in the export"
627                >Experimental factor</option>
628              <option value="spotformula"
629                title="Select a spot formula to include in the export"
630                >Spot field/formula</option>
631              <option value="assays"
632                title="Select special assays (all are still included in the export)"
633                >Bioassay selection</option>
634            </select>
635            <br>
636            <input type="checkbox" name="parameterIsRequired" id="parameterIsRequired" value="1" onclick="onFormChange()">
637            <label for="parameterIsRequired">Required</label>
638            <input type="checkbox" name="parameterIsMultiple" id="parameterIsMultiple" value="1" onclick="onFormChange()">
639            <label for="parameterIsMultiple">Multiple values</label>
640          </td>
641        </tr>
642        <tr id="minMax" style="display:none;">
643          <th>Min</th>
644          <td>
645            <input type="text" <%=clazz%> name="minValue" style="width:8em;" onchange="onFormChange()">
646            <b>Max</b>
647            <input type="text" <%=clazz%> name="maxValue" style="width:8em;" onchange="onFormChange()">
648          </td>
649        </tr>
650        <tr id="enumerationOptions" style="display:none;">
651          <th>Options</th>
652          <td>
653            <textarea name="enumerationOptions" rows="5"
654              onchange="onFormChange()" title="One option per line"></textarea>
655          </td>
656        </tr>
657        <tr id="regularDefault">
658          <th>Default value</th>
659          <td><input type="text" <%=clazz%> name="parameterDefault" 
660            onchange="onFormChange()" title="Enter a default values for the parameter"></td>
661        </tr>
662        <tr id="booleanDefault" style="display:none;">
663          <th>Default value</th>
664          <td><input type="radio" name="booleanDefault" id="booleanDefaultTrue" value="1" 
665              onclick="onFormChange()"><label for="booleanDefaultTrue">true</label>
666            <input type="radio" name="booleanDefault" id="booleanDefaultFalse" value="0" 
667              onclick="onFormChange()"><label for="booleanDefaultFalse">false</label>
668          </td>
669        </tr>
670        <tr class="dynamic">
671          <th>Description</th>
672          <td><textarea name="parameterDescription" rows="5" 
673            onchange="onFormChange()" title="Enter a description/help text for this parameter"></textarea></td>
674        </tr>
675        </table>
676       
677      </div>
678       
679      </div>
680     
681    </div>
682    </form>
683
684    <base:buttongroup subclass="dialogbuttons">     
685      <base:button onclick="saveSettings()" title="Next" />
686      <base:button onclick="doCancel()" title="Cancel" />
687    </base:buttongroup>
688  </base:body>
689  </base:page>
690  <%
691}
692finally
693{
694  if (dc != null)
695    dc.close();
696}
697%>
Note: See TracBrowser for help on using the repository browser.