/lib/galaxy/web/form_builder.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 775 lines · 705 code · 18 blank · 52 comment · 7 complexity · 6388948ac38b72286bd0ef4bd5f51926 MD5 · raw file

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