/lib/galaxy/web/form_builder.py
Python | 775 lines | 768 code | 4 blank | 3 comment | 4 complexity | 6388948ac38b72286bd0ef4bd5f51926 MD5 | raw file
1"""
2Classes for generating HTML forms
3"""
4
5import logging, sys, os, time
6
7from operator import itemgetter
8from cgi import escape
9from galaxy.util import restore_text, relpath, nice_size, unicodify
10from galaxy.web import url_for
11from binascii import hexlify
12
13log = logging.getLogger(__name__)
14
15class BaseField(object):
16 def get_html( self, prefix="" ):
17 """Returns the html widget corresponding to the parameter"""
18 raise TypeError( "Abstract Method" )
19 def get_disabled_str( self, disabled=False ):
20 if disabled:
21 return ' disabled="disabled"'
22 else:
23 return ''
24
25class TextField(BaseField):
26 """
27 A standard text input box.
28
29 >>> print TextField( "foo" ).get_html()
30 <input type="text" name="foo" size="10" value="">
31 >>> print TextField( "bins", size=4, value="default" ).get_html()
32 <input type="text" name="bins" size="4" value="default">
33 """
34 def __init__( self, name, size=None, value=None ):
35 self.name = name
36 self.size = int( size or 10 )
37 self.value = value or ""
38 def get_html( self, prefix="", disabled=False ):
39 value = self.value
40 if not isinstance( value, basestring ):
41 value = str( value )
42 value = unicodify( value )
43 return unicodify( '<input type="text" name="%s%s" size="%d" value="%s"%s>' \
44 % ( prefix, self.name, self.size, escape( value, quote=True ), self.get_disabled_str( disabled ) ) )
45 def set_size(self, size):
46 self.size = int( size )
47
48class PasswordField(BaseField):
49 """
50 A password input box. text appears as "******"
51
52 >>> print PasswordField( "foo" ).get_html()
53 <input type="password" name="foo" size="10" value="">
54 >>> print PasswordField( "bins", size=4, value="default" ).get_html()
55 <input type="password" name="bins" size="4" value="default">
56 """
57 def __init__( self, name, size=None, value=None ):
58 self.name = name
59 self.size = int( size or 10 )
60 self.value = value or ""
61 def get_html( self, prefix="", disabled=False ):
62 return unicodify( '<input type="password" name="%s%s" size="%d" value="%s"%s>' \
63 % ( prefix, self.name, self.size, escape( str( self.value ), quote=True ), self.get_disabled_str( disabled ) ) )
64 def set_size(self, size):
65 self.size = int( size )
66
67class TextArea(BaseField):
68 """
69 A standard text area box.
70
71 >>> print TextArea( "foo" ).get_html()
72 <textarea name="foo" rows="5" cols="25"></textarea>
73 >>> print TextArea( "bins", size="4x5", value="default" ).get_html()
74 <textarea name="bins" rows="4" cols="5">default</textarea>
75 """
76 _DEFAULT_SIZE = "5x25"
77 def __init__( self, name, size=None, value=None ):
78 self.name = name
79 size = size or self._DEFAULT_SIZE
80 self.size = size.split("x")
81 self.rows = int(self.size[0])
82 self.cols = int(self.size[-1])
83 self.value = value or ""
84 def get_html( self, prefix="", disabled=False ):
85 return unicodify( '<textarea name="%s%s" rows="%d" cols="%d"%s>%s</textarea>' \
86 % ( prefix, self.name, self.rows, self.cols, self.get_disabled_str( disabled ), escape( str( self.value ), quote=True ) ) )
87 def set_size(self, rows, cols):
88 self.rows = rows
89 self.cols = cols
90
91class CheckboxField(BaseField):
92 """
93 A checkbox (boolean input)
94
95 >>> print CheckboxField( "foo" ).get_html()
96 <input type="checkbox" id="foo" name="foo" value="true"><input type="hidden" name="foo" value="true">
97 >>> print CheckboxField( "bar", checked="yes" ).get_html()
98 <input type="checkbox" id="bar" name="bar" value="true" checked="checked"><input type="hidden" name="bar" value="true">
99 """
100 def __init__( self, name, checked=None, refresh_on_change = False, refresh_on_change_values = None ):
101 self.name = name
102 self.checked = ( checked == True ) or ( isinstance( checked, basestring ) and ( checked.lower() in ( "yes", "true", "on" ) ) )
103 self.refresh_on_change = refresh_on_change
104 self.refresh_on_change_values = refresh_on_change_values or []
105 if self.refresh_on_change:
106 self.refresh_on_change_text = ' refresh_on_change="true" '
107 if self.refresh_on_change_values:
108 self.refresh_on_change_text = '%s refresh_on_change_values="%s" ' % ( self.refresh_on_change_text, ",".join( self.refresh_on_change_values ) )
109 else:
110 self.refresh_on_change_text = ''
111 def get_html( self, prefix="", disabled=False ):
112 if self.checked:
113 checked_text = ' checked="checked"'
114 else:
115 checked_text = ""
116 id_name = prefix + self.name
117 # The hidden field is necessary because if the check box is not checked on the form, it will
118 # not be included in the request params. The hidden field ensure that this will happen. When
119 # parsing the request, the value 'true' in the hidden field actually means it is NOT checked.
120 # See the is_checked() method below. The prefix is necessary in each case to ensure functional
121 # correctness when the param is inside a conditional.
122 return unicodify( '<input type="checkbox" id="%s" name="%s" value="true"%s%s%s><input type="hidden" name="%s%s" value="true"%s>' \
123 % ( id_name, id_name, checked_text, self.get_disabled_str( disabled ), self.refresh_on_change_text, prefix, self.name, self.get_disabled_str( disabled ) ) )
124 @staticmethod
125 def is_checked( value ):
126 if value == True:
127 return True
128 # This may look strange upon initial inspection, but see the comments in the get_html() method
129 # above for clarification. Basically, if value is not True, then it will always be a list with
130 # 2 input fields ( a checkbox and a hidden field ) if the checkbox is checked. If it is not
131 # checked, then value will be only the hidden field.
132 return isinstance( value, list ) and len( value ) == 2
133 def set_checked(self, value):
134 if isinstance( value, basestring ):
135 self.checked = value.lower() in [ "yes", "true", "on" ]
136 else:
137 self.checked = value
138
139class FileField(BaseField):
140 """
141 A file upload input.
142
143 >>> print FileField( "foo" ).get_html()
144 <input type="file" name="foo">
145 >>> print FileField( "foo", ajax = True ).get_html()
146 <input type="file" name="foo" galaxy-ajax-upload="true">
147 """
148 def __init__( self, name, value = None, ajax=False ):
149 self.name = name
150 self.ajax = ajax
151 self.value = value
152 def get_html( self, prefix="" ):
153 value_text = ""
154 if self.value:
155 value_text = ' value="%s"' % escape( str( self.value ), quote=True )
156 ajax_text = ""
157 if self.ajax:
158 ajax_text = ' galaxy-ajax-upload="true"'
159 return unicodify( '<input type="file" name="%s%s"%s%s>' % ( prefix, self.name, ajax_text, value_text ) )
160
161class FTPFileField(BaseField):
162 """
163 An FTP file upload input.
164 """
165 thead = '''
166 <table id="grid-table" class="grid">
167 <thead id="grid-table-header">
168 <tr>
169 <th id="select-header"></th>
170 <th id="name-header">
171 File
172 </th>
173 <th id="size-header">
174 Size
175 </th>
176 <th id="date-header">
177 Date
178 </th>
179 </tr>
180 </thead>
181 <tbody id="grid-table-body">
182 '''
183 trow = '''
184 <tr>
185 <td><input type="checkbox" name="%s%s" value="%s"/></td>
186 <td>%s</td>
187 <td>%s</td>
188 <td>%s</td>
189 </tr>
190 '''
191 tfoot = '''
192 </tbody>
193 </table>
194 '''
195 def __init__( self, name, dir, ftp_site, value = None ):
196 self.name = name
197 self.dir = dir
198 self.ftp_site = ftp_site
199 self.value = value
200 def get_html( self, prefix="" ):
201 rval = FTPFileField.thead
202 if self.dir is None:
203 rval += '<tr><td colspan="4"><em>Please <a href="%s">create</a> or <a href="%s">log in to</a> a Galaxy account to view files uploaded via ASPERA.</em></td></tr>' % ( url_for( controller='user', action='create', cntrller='user', referer=url_for( controller='root' ) ), url_for( controller='user', action='login', cntrller='user', referer=url_for( controller='root' ) ) )
204 elif not os.path.exists( self.dir ):
205 rval += '<tr><td colspan="4"><em>Your ASPERA upload directory contains no files.</em></td></tr>'
206 else:
207 uploads = []
208 for ( dirpath, dirnames, filenames ) in os.walk( self.dir ):
209 for filename in filenames:
210 path = relpath( os.path.join( dirpath, filename ), self.dir )
211 statinfo = os.lstat( os.path.join( dirpath, filename ) )
212 uploads.append( dict( path=path,
213 size=nice_size( statinfo.st_size ),
214 ctime=time.strftime( "%m/%d/%Y %I:%M:%S %p", time.localtime( statinfo.st_ctime ) ) ) )
215 if not uploads:
216 rval += '<tr><td colspan="4"><em>Your ASPERA upload directory contains no files.</em></td></tr>'
217 uploads = sorted(uploads, key=itemgetter("path"))
218 for upload in uploads:
219 rval += FTPFileField.trow % ( prefix, self.name, upload['path'], upload['path'], upload['size'], upload['ctime'] )
220 rval += FTPFileField.tfoot
221 rval += '<div class="toolParamHelp">This Galaxy server allows you to upload files via ASPERA. To upload some files, log in to the ASPERA server at <strong>%s</strong> using your Cistrome credentials (email address and password).</div>' % self.ftp_site
222 return rval
223
224class HiddenField(BaseField):
225 """
226 A hidden field.
227
228 >>> print HiddenField( "foo", 100 ).get_html()
229 <input type="hidden" name="foo" value="100">
230 """
231 def __init__( self, name, value=None ):
232 self.name = name
233 self.value = value or ""
234 def get_html( self, prefix="" ):
235 return unicodify( '<input type="hidden" name="%s%s" value="%s">' % ( prefix, self.name, escape( str( self.value ), quote=True ) ) )
236
237class SelectField(BaseField):
238 """
239 A select field.
240
241 >>> t = SelectField( "foo", multiple=True )
242 >>> t.add_option( "tuti", 1 )
243 >>> t.add_option( "fruity", "x" )
244 >>> print t.get_html()
245 <select name="foo" multiple>
246 <option value="1">tuti</option>
247 <option value="x">fruity</option>
248 </select>
249
250 >>> t = SelectField( "bar" )
251 >>> t.add_option( "automatic", 3 )
252 >>> t.add_option( "bazooty", 4, selected=True )
253 >>> print t.get_html()
254 <select name="bar" last_selected_value="4">
255 <option value="3">automatic</option>
256 <option value="4" selected>bazooty</option>
257 </select>
258
259 >>> t = SelectField( "foo", display="radio" )
260 >>> t.add_option( "tuti", 1 )
261 >>> t.add_option( "fruity", "x" )
262 >>> print t.get_html()
263 <div><input type="radio" name="foo" value="1" id="foo|1"><label class="inline" for="foo|1">tuti</label></div>
264 <div><input type="radio" name="foo" value="x" id="foo|x"><label class="inline" for="foo|x">fruity</label></div>
265
266 >>> t = SelectField( "bar", multiple=True, display="checkboxes" )
267 >>> t.add_option( "automatic", 3 )
268 >>> t.add_option( "bazooty", 4, selected=True )
269 >>> print t.get_html()
270 <div class="checkUncheckAllPlaceholder" checkbox_name="bar"></div>
271 <div><input type="checkbox" name="bar" value="3" id="bar|3"><label class="inline" for="bar|3">automatic</label></div>
272 <div><input type="checkbox" name="bar" value="4" id="bar|4" checked='checked'><label class="inline" for="bar|4">bazooty</label></div>
273 """
274 def __init__( self, name, multiple=None, display=None, refresh_on_change=False, refresh_on_change_values=None, size=None ):
275 self.name = name
276 self.multiple = multiple or False
277 self.size = size
278 self.options = list()
279 if display == "checkboxes":
280 assert multiple, "Checkbox display only supported for multiple select"
281 elif display == "radio":
282 assert not( multiple ), "Radio display only supported for single select"
283 elif display is not None:
284 raise Exception, "Unknown display type: %s" % display
285 self.display = display
286 self.refresh_on_change = refresh_on_change
287 self.refresh_on_change_values = refresh_on_change_values or []
288 if self.refresh_on_change:
289 self.refresh_on_change_text = ' refresh_on_change="true"'
290 if self.refresh_on_change_values:
291 self.refresh_on_change_text = '%s refresh_on_change_values="%s"' % ( self.refresh_on_change_text, escape( ",".join( self.refresh_on_change_values ), quote=True ) )
292 else:
293 self.refresh_on_change_text = ''
294 def add_option( self, text, value, selected = False ):
295 self.options.append( ( text, value, selected ) )
296 def get_html( self, prefix="", disabled=False ):
297 if self.display == "checkboxes":
298 return self.get_html_checkboxes( prefix, disabled )
299 elif self.display == "radio":
300 return self.get_html_radio( prefix, disabled )
301 else:
302 return self.get_html_default( prefix, disabled )
303 def get_html_checkboxes( self, prefix="", disabled=False ):
304 rval = []
305 ctr = 0
306 if len( self.options ) > 1:
307 rval.append ( '<div class="checkUncheckAllPlaceholder" checkbox_name="%s%s"></div>' % ( prefix, self.name ) ) #placeholder for the insertion of the Select All/Unselect All buttons
308 for text, value, selected in self.options:
309 style = ""
310 if not isinstance( value, basestring ):
311 value = str( value )
312 if not isinstance( text, basestring ):
313 text = str( text )
314 text = unicodify( text )
315 escaped_value = escape( unicodify( value ), quote=True )
316 uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
317 if len(self.options) > 2 and ctr % 2 == 1:
318 style = " class=\"odd_row\""
319 selected_text = ""
320 if selected:
321 selected_text = " checked='checked'"
322 rval.append( '<div%s><input type="checkbox" name="%s%s" value="%s" id="%s"%s%s><label class="inline" for="%s">%s</label></div>' % \
323 ( style, prefix, self.name, escaped_value, uniq_id, selected_text, self.get_disabled_str( disabled ), uniq_id, escape( text, quote=True ) ) )
324 ctr += 1
325 return unicodify( "\n".join( rval ) )
326 def get_html_radio( self, prefix="", disabled=False ):
327 rval = []
328 ctr = 0
329 for text, value, selected in self.options:
330 style = ""
331 escaped_value = escape( str( value ), quote=True )
332 uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
333 if len(self.options) > 2 and ctr % 2 == 1:
334 style = " class=\"odd_row\""
335 selected_text = ""
336 if selected:
337 selected_text = " checked='checked'"
338 rval.append( '<div%s><input type="radio" name="%s%s"%s value="%s" id="%s"%s%s><label class="inline" for="%s">%s</label></div>' % \
339 ( style,
340 prefix,
341 self.name,
342 self.refresh_on_change_text,
343 escaped_value,
344 uniq_id,
345 selected_text,
346 self.get_disabled_str( disabled ),
347 uniq_id,
348 text ) )
349 ctr += 1
350 return unicodify( "\n".join( rval ) )
351 def get_html_default( self, prefix="", disabled=False ):
352 if self.multiple:
353 multiple = " multiple"
354 else:
355 multiple = ""
356 if self.size:
357 size = ' size="%s"' % str( self.size )
358 else:
359 size = ''
360 rval = []
361 last_selected_value = ""
362 for text, value, selected in self.options:
363 if selected:
364 selected_text = " selected"
365 last_selected_value = value
366 if not isinstance( last_selected_value, basestring ):
367 last_selected_value = str( last_selected_value )
368 else:
369 selected_text = ""
370 if not isinstance( value, basestring ):
371 value = str( value )
372 if not isinstance( text, basestring ):
373 text = str( text )
374 rval.append( '<option value="%s"%s>%s</option>' % ( escape( unicodify( value ), quote=True ), selected_text, escape( unicodify( text ), quote=True ) ) )
375 if last_selected_value:
376 last_selected_value = ' last_selected_value="%s"' % escape( unicodify( last_selected_value ), quote=True )
377 rval.insert( 0, '<select name="%s%s"%s%s%s%s%s>' % \
378 ( prefix, self.name, multiple, size, self.refresh_on_change_text, last_selected_value, self.get_disabled_str( disabled ) ) )
379 rval.append( '</select>' )
380 return unicodify( "\n".join( rval ) )
381 def get_selected( self, return_label=False, return_value=False, multi=False ):
382 '''
383 Return the currently selected option's label, value or both as a tuple. For
384 multi-select lists, a list is returned.
385 '''
386 if multi:
387 selected_options = []
388 for label, value, selected in self.options:
389 if selected:
390 if return_label and return_value:
391 if multi:
392 selected_options.append( ( label, value ) )
393 else:
394 return ( label, value )
395 elif return_label:
396 if multi:
397 selected_options.append( label )
398 else:
399 return label
400 elif return_value:
401 if multi:
402 selected_options.append( value )
403 else:
404 return value
405 if multi:
406 return selected_options
407 return None
408
409class DrillDownField( BaseField ):
410 """
411 A hierarchical select field, which allows users to 'drill down' a tree-like set of options.
412
413 >>> t = DrillDownField( "foo", multiple=True, display="checkbox", options=[{'name': 'Heading 1', 'value': 'heading1', 'options': [{'name': 'Option 1', 'value': 'option1', 'options': []}, {'name': 'Option 2', 'value': 'option2', 'options': []}, {'name': 'Heading 1', 'value': 'heading1', 'options': [{'name': 'Option 3', 'value': 'option3', 'options': []}, {'name': 'Option 4', 'value': 'option4', 'options': []}]}]}, {'name': 'Option 5', 'value': 'option5', 'options': []}] )
414 >>> print t.get_html()
415 <div class="form-row drilldown-container" id="drilldown--666f6f">
416 <div class="form-row-input">
417 <div><span class="form-toggle icon-button toggle-expand" id="drilldown--666f6f-68656164696e6731-click"></span>
418 <input type="checkbox" name="foo" value="heading1" >Heading 1
419 </div><div class="form-row" id="drilldown--666f6f-68656164696e6731-container" style="float: left; margin-left: 1em;">
420 <div class="form-row-input">
421 <input type="checkbox" name="foo" value="option1" >Option 1
422 </div>
423 <div class="form-row-input">
424 <input type="checkbox" name="foo" value="option2" >Option 2
425 </div>
426 <div class="form-row-input">
427 <div><span class="form-toggle icon-button toggle-expand" id="drilldown--666f6f-68656164696e6731-68656164696e6731-click"></span>
428 <input type="checkbox" name="foo" value="heading1" >Heading 1
429 </div><div class="form-row" id="drilldown--666f6f-68656164696e6731-68656164696e6731-container" style="float: left; margin-left: 1em;">
430 <div class="form-row-input">
431 <input type="checkbox" name="foo" value="option3" >Option 3
432 </div>
433 <div class="form-row-input">
434 <input type="checkbox" name="foo" value="option4" >Option 4
435 </div>
436 </div>
437 </div>
438 </div>
439 </div>
440 <div class="form-row-input">
441 <input type="checkbox" name="foo" value="option5" >Option 5
442 </div>
443 </div>
444 >>> t = DrillDownField( "foo", multiple=False, display="radio", options=[{'name': 'Heading 1', 'value': 'heading1', 'options': [{'name': 'Option 1', 'value': 'option1', 'options': []}, {'name': 'Option 2', 'value': 'option2', 'options': []}, {'name': 'Heading 1', 'value': 'heading1', 'options': [{'name': 'Option 3', 'value': 'option3', 'options': []}, {'name': 'Option 4', 'value': 'option4', 'options': []}]}]}, {'name': 'Option 5', 'value': 'option5', 'options': []}] )
445 >>> print t.get_html()
446 <div class="form-row drilldown-container" id="drilldown--666f6f">
447 <div class="form-row-input">
448 <div><span class="form-toggle icon-button toggle-expand" id="drilldown--666f6f-68656164696e6731-click"></span>
449 <input type="radio" name="foo" value="heading1" >Heading 1
450 </div><div class="form-row" id="drilldown--666f6f-68656164696e6731-container" style="float: left; margin-left: 1em;">
451 <div class="form-row-input">
452 <input type="radio" name="foo" value="option1" >Option 1
453 </div>
454 <div class="form-row-input">
455 <input type="radio" name="foo" value="option2" >Option 2
456 </div>
457 <div class="form-row-input">
458 <div><span class="form-toggle icon-button toggle-expand" id="drilldown--666f6f-68656164696e6731-68656164696e6731-click"></span>
459 <input type="radio" name="foo" value="heading1" >Heading 1
460 </div><div class="form-row" id="drilldown--666f6f-68656164696e6731-68656164696e6731-container" style="float: left; margin-left: 1em;">
461 <div class="form-row-input">
462 <input type="radio" name="foo" value="option3" >Option 3
463 </div>
464 <div class="form-row-input">
465 <input type="radio" name="foo" value="option4" >Option 4
466 </div>
467 </div>
468 </div>
469 </div>
470 </div>
471 <div class="form-row-input">
472 <input type="radio" name="foo" value="option5" >Option 5
473 </div>
474 </div>
475 """
476 def __init__( self, name, multiple=None, display=None, refresh_on_change=False, options = [], value = [], refresh_on_change_values = [] ):
477 self.name = name
478 self.multiple = multiple or False
479 self.options = options
480 if value and not isinstance( value, list ):
481 value = [ value ]
482 elif not value:
483 value = []
484 self.value = value
485 if display == "checkbox":
486 assert multiple, "Checkbox display only supported for multiple select"
487 elif display == "radio":
488 assert not( multiple ), "Radio display only supported for single select"
489 else:
490 raise Exception, "Unknown display type: %s" % display
491 self.display = display
492 self.refresh_on_change = refresh_on_change
493 self.refresh_on_change_values = refresh_on_change_values
494 if self.refresh_on_change:
495 self.refresh_on_change_text = ' refresh_on_change="true"'
496 if self.refresh_on_change_values:
497 self.refresh_on_change_text = '%s refresh_on_change_values="%s"' % ( self.refresh_on_change_text, ",".join( self.refresh_on_change_values ) )
498 else:
499 self.refresh_on_change_text = ''
500 def get_html( self, prefix="" ):
501 def find_expanded_options( expanded_options, options, parent_options = [] ):
502 for option in options:
503 if option['value'] in self.value:
504 expanded_options.extend( parent_options )
505 if option['options']:
506 new_parents = list( parent_options ) + [ option['value'] ]
507 find_expanded_options( expanded_options, option['options'], new_parents )
508 def recurse_options( html, options, base_id, expanded_options = [] ):
509 for option in options:
510 escaped_option_value = escape( str( option['value'] ), quote=True )
511 selected = ( option['value'] in self.value )
512 if selected:
513 selected = ' checked'
514 else:
515 selected = ''
516 span_class = 'form-toggle icon-button toggle'
517 if option['value'] not in expanded_options:
518 span_class = "%s-expand" % ( span_class )
519 html.append( '<div class="form-row-input">')
520 drilldown_group_id = "%s-%s" % ( base_id, hexlify( option['value'] ) )
521 if option['options']:
522 html.append( '<div><span class="%s" id="%s-click"></span>' % ( span_class, drilldown_group_id ) )
523 html.append( '<input type="%s" name="%s%s" value="%s" %s>%s' % ( self.display, prefix, self.name, escaped_option_value, selected, option['name']) )
524 if option['options']:
525 html.append( '</div><div class="form-row" id="%s-container" style="float: left; margin-left: 1em;">' % ( drilldown_group_id ) )
526 recurse_options( html, option['options'], drilldown_group_id, expanded_options )
527 html.append( '</div>')
528 html.append( '</div>')
529 drilldown_id = "drilldown-%s-%s" % ( hexlify( prefix ), hexlify( self.name ) )
530 rval = []
531 rval.append( '<div class="form-row drilldown-container" id="%s">' % ( drilldown_id ) )
532 expanded_options = []
533 find_expanded_options( expanded_options, self.options )
534 recurse_options( rval, self.options, drilldown_id, expanded_options )
535 rval.append( '</div>' )
536 return unicodify( '\n'.join( rval ) )
537
538class AddressField(BaseField):
539 @staticmethod
540 def fields():
541 return [ ( "short_desc", "Short address description", "Required" ),
542 ( "name", "Name", "Required" ),
543 ( "institution", "Institution", "Required" ),
544 ( "address", "Address", "Required" ),
545 ( "city", "City", "Required" ),
546 ( "state", "State/Province/Region", "Required" ),
547 ( "postal_code", "Postal Code", "Required" ),
548 ( "country", "Country", "Required" ),
549 ( "phone", "Phone", "" ) ]
550 def __init__(self, name, user=None, value=None, params=None):
551 self.name = name
552 self.user = user
553 self.value = value
554 self.select_address = None
555 self.params = params
556 def get_html( self, disabled=False ):
557 address_html = ''
558 add_ids = ['none']
559 if self.user:
560 for a in self.user.addresses:
561 add_ids.append( str( a.id ) )
562 add_ids.append( 'new' )
563 self.select_address = SelectField( self.name,
564 refresh_on_change=True,
565 refresh_on_change_values=add_ids )
566 if self.value == 'none':
567 self.select_address.add_option( 'Select one', 'none', selected=True )
568 else:
569 self.select_address.add_option( 'Select one', 'none' )
570 if self.user:
571 for a in self.user.addresses:
572 if not a.deleted:
573 if self.value == str( a.id ):
574 self.select_address.add_option( a.desc, str( a.id ), selected=True )
575 # Display this address
576 address_html += '''
577 <div class="form-row">
578 %s
579 </div>
580 ''' % a.get_html()
581 else:
582 self.select_address.add_option( a.desc, str( a.id ) )
583 if self.value == 'new':
584 self.select_address.add_option( 'Add a new address', 'new', selected=True )
585 for field_name, label, help_text in self.fields():
586 add_field = TextField( self.name + '_' + field_name,
587 40,
588 restore_text( self.params.get( self.name + '_' + field_name, '' ) ) )
589 address_html += '''
590 <div class="form-row">
591 <label>%s</label>
592 %s
593 ''' % ( label, add_field.get_html( disabled=disabled ) )
594 if help_text:
595 address_html += '''
596 <div class="toolParamHelp" style="clear: both;">
597 %s
598 </div>
599 ''' % help_text
600 address_html += '''
601 </div>
602 '''
603 else:
604 self.select_address.add_option( 'Add a new address', 'new' )
605 return self.select_address.get_html( disabled=disabled ) + address_html
606
607class WorkflowField( BaseField ):
608 def __init__( self, name, user=None, value=None, params=None ):
609 self.name = name
610 self.user = user
611 self.value = value
612 self.select_workflow = None
613 self.params = params
614 def get_html( self, disabled=False ):
615 self.select_workflow = SelectField( self.name )
616 if self.value == 'none':
617 self.select_workflow.add_option( 'Select one', 'none', selected=True )
618 else:
619 self.select_workflow.add_option( 'Select one', 'none' )
620 if self.user:
621 for a in self.user.stored_workflows:
622 if not a.deleted:
623 if str( self.value ) == str( a.id ):
624 self.select_workflow.add_option( a.name, str( a.id ), selected=True )
625 else:
626 self.select_workflow.add_option( a.name, str( a.id ) )
627 return self.select_workflow.get_html( disabled=disabled )
628
629class WorkflowMappingField( BaseField):
630 def __init__( self, name, user=None, value=None, params=None, **kwd ):
631 # DBTODO integrate this with the new __build_workflow approach in requests_common. As it is, not particularly useful.
632 self.name = name
633 self.user = user
634 self.value = value
635 self.select_workflow = None
636 self.params = params
637 self.workflow_inputs = []
638 def get_html( self, disabled=False ):
639 self.select_workflow = SelectField( self.name, refresh_on_change = True )
640 workflow_inputs = []
641 if self.value == 'none':
642 self.select_workflow.add_option( 'Select one', 'none', selected=True )
643 else:
644 self.select_workflow.add_option( 'Select one', 'none' )
645 if self.user:
646 for a in self.user.stored_workflows:
647 if not a.deleted:
648 if str( self.value ) == str( a.id ):
649 self.select_workflow.add_option( a.name, str( a.id ), selected=True )
650 else:
651 self.select_workflow.add_option( a.name, str( a.id ) )
652 if self.value and self.value != 'none':
653 # Workflow selected. Find all inputs.
654 for workflow in self.user.stored_workflows:
655 if workflow.id == int(self.value):
656 for step in workflow.latest_workflow.steps:
657 if step.type == 'data_input':
658 if step.tool_inputs and "name" in step.tool_inputs:
659 workflow_inputs.append((step.tool_inputs['name'], TextField( '%s_%s' % (self.name, step.id), 20)))
660 # Do something more appropriate here and allow selection of inputs
661 return self.select_workflow.get_html( disabled=disabled ) + ''.join(['<div class="form-row"><label>%s</label>%s</div>' % (s[0], s[1].get_html()) for s in workflow_inputs])
662 def get_display_text(self):
663 if self.value:
664 return self.value
665 else:
666 return '-'
667class HistoryField( BaseField ):
668 def __init__( self, name, user=None, value=None, params=None ):
669 self.name = name
670 self.user = user
671 self.value = value
672 self.select_history = None
673 self.params = params
674 def get_html( self, disabled=False ):
675 self.select_history = SelectField( self.name )
676 if self.value == 'none':
677 self.select_history.add_option( 'No Import', 'none', selected=True )
678 self.select_history.add_option( 'New History', 'new' )
679 else:
680 self.select_history.add_option( 'No Import', 'none' )
681 if self.value == 'new':
682 self.select_history.add_option( 'New History', 'new', selected=True )
683 else:
684 self.select_history.add_option( 'New History', 'new')
685 if self.user:
686 for a in self.user.histories:
687 if not a.deleted:
688 if str( self.value ) == str( a.id ):
689 self.select_history.add_option( a.name, str( a.id ), selected=True )
690 else:
691 self.select_history.add_option( a.name, str( a.id ) )
692 return self.select_history.get_html( disabled=disabled )
693 def get_display_text(self):
694 if self.value:
695 return self.value
696 else:
697 return '-'
698
699class LibraryField( BaseField ):
700 def __init__( self, name, value=None, trans=None ):
701 self.name = name
702 self.lddas = value
703 self.trans = trans
704 def get_html( self, prefix="", disabled=False ):
705 if not self.lddas:
706 ldda_ids = ""
707 text = "Select library dataset(s)"
708 else:
709 ldda_ids = "||".join( [ self.trans.security.encode_id( ldda.id ) for ldda in self.lddas ] )
710 text = "<br />".join( [ "%s. %s" % (i+1, ldda.name) for i, ldda in enumerate(self.lddas)] )
711 return unicodify( '<a href="javascript:void(0);" class="add-librarydataset">%s</a> \
712 <input type="hidden" name="%s%s" value="%s">' % ( text, prefix, self.name, escape( str(ldda_ids), quote=True ) ) )
713
714 def get_display_text(self):
715 if self.ldda:
716 return self.ldda.name
717 else:
718 return 'None'
719
720def get_suite():
721 """Get unittest suite for this module"""
722 import doctest, sys
723 return doctest.DocTestSuite( sys.modules[__name__] )
724
725# --------- Utility methods -----------------------------
726
727def build_select_field( trans, objs, label_attr, select_field_name, initial_value='none',
728 selected_value='none', refresh_on_change=False, multiple=False, display=None, size=None ):
729 """
730 Build a SelectField given a set of objects. The received params are:
731
732 - objs: the set of objects used to populate the option list
733 - label_attr: the attribute of each obj (e.g., name, email, etc ) whose value is used to populate each option label.
734
735 - If the string 'self' is passed as label_attr, each obj in objs is assumed to be a string, so the obj itself is used
736
737 - select_field_name: the name of the SelectField
738 - initial_value: the value of the first option in the SelectField - allows for an option telling the user to select something
739 - selected_value: the value of the currently selected option
740 - refresh_on_change: True if the SelectField should perform a refresh_on_change
741 """
742 if initial_value == 'none':
743 values = [ initial_value ]
744 else:
745 values = []
746 for obj in objs:
747 if label_attr == 'self':
748 # Each obj is a string
749 values.append( obj )
750 else:
751 values.append( trans.security.encode_id( obj.id ) )
752 if refresh_on_change:
753 refresh_on_change_values = values
754 else:
755 refresh_on_change_values = []
756 select_field = SelectField( name=select_field_name,
757 multiple=multiple,
758 display=display,
759 refresh_on_change=refresh_on_change,
760 refresh_on_change_values=refresh_on_change_values,
761 size=size )
762 for obj in objs:
763 if label_attr == 'self':
764 # Each obj is a string
765 if str( selected_value ) == str( obj ):
766 select_field.add_option( obj, obj, selected=True )
767 else:
768 select_field.add_option( obj, obj )
769 else:
770 label = getattr( obj, label_attr )
771 if str( selected_value ) == str( obj.id ) or str( selected_value ) == trans.security.encode_id( obj.id ):
772 select_field.add_option( label, trans.security.encode_id( obj.id ), selected=True )
773 else:
774 select_field.add_option( label, trans.security.encode_id( obj.id ) )
775 return select_field