Attachment 'Form-0.11alpha.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     Form.py - MoinMoin macro to display database based forms
   4     
   5     @copyright: 2008 Wolfgang Fischer
   6     @license: GNU GPL, see COPYING for details.
   7 
   8     Information on prerequisites, installation and usage can be found at
   9         http://moinmo.in/MacroMarket/Form
  10         
  11     2008-07-29 RudolfReuter, Version 0.9alpha because of moin-1.7.1 changes 
  12       Attention: Python >= 2.5 needed for SQLite access
  13       debug help for tracing, insert the following line
  14           import pdb; pdb.set_trace() #debug
  15       changes in moin-1.7.1:
  16       1. MoinMoin/wikidicts.py - Function get_dict was dropped
  17       2. MoinMoin/wikiutil.py - Function renderText, 4th parameter dropped
  18       Added ".type" check in first Form element. 2008-07-31
  19     2008-12-28 RudolfReuter, Version 0.10alpha because of moin-1.8.1 changes 
  20       added dict check xxx/FormsDict for Linux version at #Get form dict
  21     2010-01-28 RudolfReuter, adopted to 1.9.1 (Werkzeug), version 0.11alpha
  22 """
  23 Dependencies = ['external']  # Can't be cached as it depends on external data
  24 
  25 def execute(macro, args):
  26     args = [ arg.strip() for arg in args.split(',') ]
  27     request = macro.request
  28     if not hasattr(request, 'form_macro_form'):
  29         request.form_macro_form = Form(request)
  30     return request.form_macro_form.render(args)
  31 
  32 
  33 class Form:
  34     import datetime
  35     from MoinMoin import wikiutil
  36 
  37     # fake _ function to get gettext recognize those texts:
  38     _ = lambda x: x
  39     ui_text = {
  40         # TODO: Button labels should be defined here
  41         }
  42     del _
  43 
  44     datetime_types = [ datetime.datetime, datetime.date, datetime.time ]
  45 
  46 
  47     def __init__(self, request):
  48         self.request = request
  49         self.formatter = request.formatter
  50         self.post_args = request.form
  51         self.date_format = request.user.date_fmt or request.cfg.date_fmt
  52         self.datetime_format = request.user.datetime_fmt or request.cfg.datetime_fmt
  53         today = self.datetime.date.today()
  54         self.variables = {
  55             'PAGE': request.page.page_name,
  56             'NOW' : self.datetime.datetime.now(),
  57             'TODAY' : today,
  58             'TODAY_DATETIME' : self.datetime.datetime.fromordinal(today.toordinal()),
  59             'ME': request.user.name,
  60         }
  61         self.forms = {}
  62         self.dict_name_stack = []
  63         self.last_form_key = ''
  64         
  65         from MoinMoin import version
  66         self.is_moin_1_7 = version.release.startswith('1.7')
  67         self.is_moin_1_8 = version.release.startswith('1.8')
  68         self.is_moin_1_9 = version.release.startswith('1.9')
  69 
  70         # Extract information on post action
  71         self.action = u''
  72         self.action_form = u''
  73         self.action_index = 0
  74         for k in self.post_args.keys():
  75             if k.startswith('_Action_') and len(k) > 18:
  76                 self.action = k[8:11]
  77                 self.action_index = int(k[12:16])
  78                 self.action_form = k[17:]
  79 
  80 
  81 
  82     # =============================
  83     # Render form elements
  84     # =============================
  85     
  86     def render(self, args):
  87         if not args:
  88             return self.error_message('Missing arguments')
  89         element = args[0].lower()
  90         if element == 'start':
  91             return self.render_start()
  92         elif element == 'end':
  93             return self.render_end()
  94         request = self.request
  95         post_args = self.post_args
  96 
  97         if len(args) > 1:  # key specified
  98             key = self.key = args[1]
  99         else:
 100             key = u''
 101         self.dict_name, self.form_name, self.field_name = self.parse_element_key(key, element)
 102         dict_name = self.dict_name
 103         form_name = self.form_name
 104         self.form_key = form_key = u'%s.%s' % (dict_name, form_name)
 105         self.last_form_key = self.form_key
 106         if form_key not in self.forms:  # First element of this form
 107             form = self.form = self.forms[form_key] = {}
 108             form['form_name'] = form_name
 109             form['dict_name'] = dict_name
 110             # Get form dict
 111             if self.is_moin_1_7 or self.is_moin_1_8:
 112                 if not request.dicts.has_dict(dict_name): # fix for Linux, RudolfReuter 2008-12-28
 113                     request.dicts.adddict(request, dict_name)
 114 
 115             if self.is_moin_1_7 or self.is_moin_1_8:
 116                 form_dict_obj = request.dicts.dict(dict_name) 
 117             elif self.is_moin_1_9:
 118                 form_dict_obj = request.dicts[dict_name] 
 119             #if not hasattr(form_dict_obj, 'get_dict'): # 'get_dict' was dropped in moin-1.7.1
 120             #    form['dict'] = { }
 121             #    return self.error_message('The form dictionary "%s" does not exist or is not valid' % dict_name)
 122             if not form_dict_obj.has_key('.type'):
 123                 form['dict'] = { }
 124                 return self.error_message('The form dictionary "%s" does not exist or is not valid' % dict_name)
 125             #form['dict'] = form_dict_obj.get_dict()
 126             form['dict'] = form_dict_obj
 127             form['addonly'] = self.get_stripped_from_form_dict('addonly') == u'1'
 128             # Connect to data source
 129             error = self.connect()
 130             if error:
 131                 return self.error_message(error)
 132 
 133             # Fetch data
 134             error = self.fetch_data()
 135             if error:
 136                 return self.error_message(error)
 137 
 138             if form_key == self.action_form:
 139                 # Do form action
 140                 self.do_action()
 141                 # Fetch updated data
 142                 self.fetch_data()
 143             # Close connection
 144             self.connection.close()
 145             
 146             records = form['records']
 147             form['add_mode'] = (post_args.has_key('_Action_New_0000_%s' % form_key) or not records) and self.get_stripped_from_form_dict('insert')
 148             # Set index of current record
 149             self.set_record_index()
 150             # Add record values to variables dict
 151             # if form_key not in post_args.get('form', []):
 152             index = form['index']
 153             if 0 < index <= len(records):
 154                 record = records[index-1]
 155             else:
 156                 record = value = None
 157             for (field, field_data) in form['fields'].iteritems():
 158                 field_type = field_data[1]
 159                 if record:
 160                     value = record[field_data[0]]
 161                     if field_type not in self.datetime_types and field_type:
 162                         value = field_type(value)
 163                 self.variables['%s.%s' % (form_key, field)] = value
 164            
 165         self.form = self.forms[form_key]
 166         
 167         if element == 'form':
 168             return self.render_form()
 169         elif element == 'buttons':
 170             return self.render_buttons()
 171         elif element == 'navigation':
 172             return self.render_navigation()
 173         elif element == 'filter':
 174             return self.render_filter()
 175         elif element == 'next':
 176             return self.render_next()
 177         else:
 178             return self.render_field(element, args)
 179 
 180         
 181     def render_form(self):
 182         # TODO: handle recursion
 183         # TODO: handle form body doesn't exist
 184         form = self.form
 185         request = self.request
 186         form_text = [ self.get_stripped_from_form_dict('header').replace('\\n', '\n') % self.variables ]
 187         form_body = self.get_stripped_from_form_dict('body').replace('\\n', '\n') % self.variables
 188         if self.get_stripped_from_form_dict('all'):
 189             records = form['records']
 190             count = [len(records), len(records) + 1][self.get_stripped_from_form_dict('insert') != u'']
 191             for i in range(count):
 192                 form_text.append(form_body)
 193         else:
 194             form_text.append(form_body)
 195         form_text.append(self.get_stripped_from_form_dict('footer').replace('\\n', '\n') % self.variables)
 196         # Load the parser
 197         Parser = self.wikiutil.searchAndImportPlugin(request.cfg, "parser", 'wiki')
 198         self.dict_name_stack.append(self.dict_name)
 199         # change in moin-1.7.1 MoinMoin/wikiutil.py line 2476, 4th parameter dropped
 200         #result = self.wikiutil.renderText(request, Parser, u''.join(form_text), line_anchors=False)
 201         result = self.wikiutil.renderText(request, Parser, u''.join(form_text))
 202         self.dict_name_stack.pop()
 203         return result
 204         
 205         
 206     def render_start(self):
 207         return self.formatter.rawHTML('<span><form action="" method="POST" enctype="multipart/form-data">')
 208 
 209         
 210     def render_end(self):
 211         return self.formatter.rawHTML('</form></span>')
 212   
 213     
 214     def render_next(self):
 215         form = self.form
 216         form['index'] += 1
 217         form['add_mode'] = form['index'] > len(form['records'])
 218         return u''
 219 
 220 
 221     def render_field(self, element, args):
 222         form = self.form
 223         field_name = self.field_name
 224         field_key = '%s.%s' % (self.form_key, field_name)
 225         value = self.wikiutil.escape(self.get_field_value(self.form_key, field_name, field_key) or '', 1)
 226         
 227         if element == 'value':
 228             return self.formatter.rawHTML(u'%s<input type="hidden" name="%s" value="%s">' % (value, field_key, value))
 229 
 230         elif element == 'textarea':
 231             if len(args) > 2:
 232                 cols = args[2]
 233             else:
 234                 cols = u''
 235             if len(args) > 3:
 236                 rows = args[3]
 237             else:
 238                 rows = u''
 239             return self.formatter.rawHTML(u'<textarea name="%s" cols="%s" rows="%s">%s</textarea>' % (field_key, cols, rows, value))
 240 
 241         elif element == 'hidden':
 242             return self.formatter.rawHTML(u'<input type="hidden" name="%s" value="%s">' % (field_key, value))
 243 
 244         else:
 245             if len(args) > 2:
 246                 size = args[2]
 247             else:
 248                 size = u''
 249             return self.formatter.rawHTML(u'<input type="text" name="%s" size="%s" value="%s">' % (field_key, size, value))
 250 
 251 
 252     def get_field_value(self, form_key, field_name, field_key, for_action = 0):
 253         if field_name.startswith('@'):
 254             value = self.post_args.get(field_key, [None])[0]
 255         elif not for_action:
 256             form = self.forms[form_key]
 257             index = form['index']
 258             records = form['records']
 259             if index > len(records):
 260                 value = None
 261             else:
 262                 value = records[index-1][form['fields'][field_name][0]]
 263         else:
 264             if form_key == self.action_form:
 265                 index = self.action_index
 266             else:
 267                 index = 0 
 268             value = self.post_args.get(field_key, [u''])[index]
 269             form = self.forms[form_key]
 270             field_type = form['fields'][field_name][1]
 271             if field_type:
 272                 value = field_type(value)
 273         if value == '':
 274             value = None
 275         return value
 276 
 277 
 278     def render_navigation(self):
 279         form = self.form
 280         form_key = self.form_key
 281         return self.formatter.rawHTML("""
 282                     <input type="submit" name="First_%s" value="|<">
 283                     <input type="submit" name="Previous_%s" value="<">
 284                     <input type="text" name="GoTo_%s" size="3" value="%s">
 285                     <input type="submit" name="Next_%s" value=">">
 286                     <input type="submit" name="Last_%s" value=">|"> of %s
 287                     """ % (form_key, form_key, form_key, str(form['index']), form_key, form_key, len(form['records'])))
 288 
 289 
 290     def render_filter(self):
 291         formatter = self.formatter
 292         return formatter.rawHTML('<input type="submit" name="_Action_App_0000" value="Filter"><input type="reset">')
 293 
 294 
 295     def render_buttons(self):
 296         formatter = self.formatter
 297         if self.get_stripped_from_form_dict('all'):
 298             action_index = self.form['index'] - 1
 299         else:
 300             action_index = 0
 301         action_key = ('0000%i_' % action_index)[-5:]
 302         action_key += self.form_key
 303         result = []
 304 #        if self.form['unbound']:
 305 #            result.append(formatter.rawHTML('<input type="submit" name="_Action_App_%s" value="Filter"><input type="reset">' % (action_key)))
 306         if self.form['add_mode']:
 307             result.append(formatter.rawHTML('<input type="submit" name="_Action_Add_%s" value="Add"><input type="submit" name="_Action_Can_%s" value="Cancel">' % (action_key, action_key)))
 308         else:
 309             if self.get_stripped_from_form_dict('update'):
 310                 result.append(formatter.rawHTML('<input type="submit" name="_Action_Sav_%s" value="Save">' % action_key))
 311             if self.get_stripped_from_form_dict('delete'):
 312                 result.append(formatter.rawHTML('<input type="submit" name="_Action_Del_%s" value="Delete">' % action_key))
 313             if self.get_stripped_from_form_dict('insert') and not self.get_stripped_from_form_dict('all'):
 314                 result.append(formatter.rawHTML('<input type="submit" name="_Action_New_%s" value="New">' % action_key))
 315         return u''.join(result)
 316 
 317 
 318 
 319     # =============================
 320     # DataBase communication
 321     # =============================
 322 
 323     def fetch_data(self):
 324         select_query = self.get_stripped_from_form_dict('select')
 325         if select_query:
 326             parameters = self.get_query_parameters('select')
 327         else:
 328             return 'The form "%s" does not contain a select query' % self.form_key
 329         cursor = self.connection.cursor()
 330         cursor.execute(select_query, parameters)
 331         description = cursor.description
 332         form = self.form
 333         form['fields'] = dict([(description[i][0], [i, description[i][1]]) for i in range(len(description))])
 334         form['records'] = [cursor.fetchall(), []][form['addonly']]
 335         cursor.close()
 336         return None
 337 
 338 
 339     def set_record_index(self):
 340         form = self.form
 341         form_key = self.form_key
 342         post_args = self.post_args
 343         records = form['records']
 344         # Calculate index of current record (first record is 1)
 345         if form['add_mode']:
 346             index = len(records) + 1
 347         elif 'GoTo_%s' % form_key in post_args:
 348             index = int(post_args['GoTo_%s' % form_key][0])
 349             if 'First_%s' % form_key in post_args:
 350                 index = 1
 351             elif 'Previous_%s' % form_key in post_args:
 352                 index -= 1
 353             elif 'Next_%s' % form_key in post_args:
 354                 index +=1
 355             elif 'Last_%s' % form_key in post_args:
 356                 index = len(records)
 357             if index < 1:
 358                 index = 1
 359             elif index > len(records):
 360                 index = len(records)
 361         else:
 362             goto_fields = self.get_stripped_from_form_dict('goto_fields')
 363             if goto_fields:
 364                 fields = form['fields']
 365                 goto_fields = [ field.strip() for field in goto_fields.split(',') ]
 366                 variables = self.variables
 367                 def get_value(value, type, variables):
 368                     value = value.strip().strip('"\'')
 369                     if value in variables:
 370                         return variables[value]
 371                     else:
 372                         value = value % variables
 373                     if type == self.datetime.datetime:
 374                         return self.datetime.datetime.strptime(value, self.datetime_format)
 375                     elif type == self.datetime.date:
 376                         return self.datetime.datetime.strptime(value, self.date_format)
 377                     return type(value)
 378                 goto_values_list = self.get_stripped_from_form_dict('goto_values').split(',')
 379                 goto_values = [ (get_value(goto_values_list[i], fields[goto_fields[i]][1], variables), fields[goto_fields[i]][0]) for i in range(len(goto_values_list)) ]
 380                 index = 0
 381                 for record_index in range(len(records)):
 382                     equal = True
 383                     for value in goto_values:
 384                         equal = value[0] == records[record_index][value[1]]
 385                         if not equal:
 386                             break
 387                     if equal:
 388                         index = record_index + 1
 389                         break
 390                 if not index:  # No matching record found
 391                     index = 1  # Set to first record (default)
 392             else:
 393                 index = 1  # Set to first record (default)
 394         form['index'] = index
 395 
 396 
 397     def do_action(self):
 398         cursor = self.connection.cursor()
 399         action = self.action
 400         action_query_map = {'Add' : 'insert', 'Sav' : 'update', 'Del' : 'delete'}
 401         if action in action_query_map:
 402             query_type =action_query_map[action] 
 403             query = self.get_stripped_from_form_dict(query_type) 
 404             if query:
 405                 parameters = self.get_query_parameters(query_type, for_action = 1)
 406                 count = cursor.execute(query, parameters)
 407         self.connection.commit()
 408         cursor.close()
 409 
 410         
 411     def connect(self):
 412         dict_name, self.connection_name = self.parse_connection_key(self.get_stripped_from_form_dict('connection'))
 413         if self.is_moin_1_7 or self.is_moin_1_8:
 414             connection_dict_obj = self.request.dicts.dict(dict_name)
 415         elif self.is_moin_1_9:
 416             connection_dict_obj = self.request.dicts[dict_name] 
 417         #if hasattr(connection_dict_obj, 'get_dict'): # 'get_dict' was dropped in moin-1.7.1
 418         self.connection_dict = connection_dict = connection_dict_obj
 419         #else:
 420             #return 'The connection dictionary "%s" does not exist or is not valid' % dict_name
 421 
 422         connection_name_prefix = '%s.' % self.connection_name
 423         connection_type = self.get_stripped_from_connection_dict('type').lower()
 424 
 425         if connection_type == 'sqlite':
 426             import sqlite3
 427             connection_string = self.get_stripped_from_connection_dict('file')
 428             if connection_string.startswith('attachment:'):
 429                 from MoinMoin.action import AttachFile
 430                 pagename, filename = AttachFile.absoluteName(connection_string[11:], dict_name)
 431                 connection_string = AttachFile.getFilename(self.request, pagename, filename)
 432             self.connection = sqlite3.connect(connection_string)
 433 
 434         elif connection_type == 'odbc':
 435             import pyodbc
 436             connection_string = self.get_stripped_from_connection_dict('connection_string')
 437             self.connection = pyodbc.connect(connection_string)
 438 
 439         elif connection_type == 'mysql':
 440             import MySQLdb
 441             port = connection_dict.get('port', '3306').strip('`')
 442             self.connection = MySQLdb.connect(host = self.get_stripped_from_connection_dict('server'), port = port, user = self.get_stripped_from_connection_dict('uid'), passwd = self.get_stripped_from_connection_dict('pwd'),db = self.get_stripped_from_connection_dict('database'))
 443 
 444         elif connection_type == 'oracle':
 445             import cx_Oracle
 446             if 'port' in parameters:
 447                 port = parameters['%sport' % connection_name_prefix]
 448             else:
 449                 port = 1521
 450             self.connection = cx_Oracle.connect(self.get_stripped_from_connection_dict('uid'), self.get_stripped_from_connection_dict('pwd'), cx_Oracle.makedsn(self.get_stripped_from_connection_dict('server'), port, self.get_stripped_from_connection_dict('database')))
 451 
 452         else:
 453             return 'Connection type "%s" specified in "%s" is unknown' % (connection_type, dict_name)
 454 
 455         return None
 456 
 457 
 458 
 459     # =============================
 460     # Handling Form Dictionaries
 461     # =============================
 462 
 463     def parse_element_key(self, key, element):
 464         key_parts = key.split('.')
 465         if element in ['form', 'buttons', 'navigation', 'filter', 'next']:
 466             key_parts.extend([u'', u'', u''])
 467         else:
 468             if len(key_parts) == 2:
 469                 key_parts.insert(0, u'')
 470             elif len(key_parts) < 2:
 471                 pass  # TODO: raise error - field not specified correctly
 472         dict_name, form_name, field_name = key_parts[:3]
 473         if not (form_name or dict_name):
 474             if self.last_form_key:
 475                 form_key = self.last_form_key
 476                 form = self.forms[form_key]
 477                 dict_name, form_name = form['dict_name'], form['form_name']
 478             else:
 479                 pass  # TODO: raise error - unspecified form
 480         elif not dict_name:
 481             if self.dict_name_stack:
 482                 dict_name = self.dict_name_stack[-1]
 483             else:
 484                 dict_name = self.request.page.page_name
 485         return dict_name, form_name, field_name
 486 
 487         
 488     def parse_connection_key(self, key):
 489         key_parts = key.split('.')
 490         key_parts.extend([u'', u''])
 491         dict_name, connection_name = key_parts[:2]
 492         if not dict_name:
 493             dict_name = self.dict_name
 494         return dict_name, connection_name
 495 
 496 
 497     def get_query_parameters(self, key, for_action = 0):
 498         # In this order:
 499         # 1. variables values
 500         # 2. form values (if not current)
 501         # 3. post values
 502         form_key = self.form_key
 503         variables = self.variables 
 504         result = []
 505         parameters = self.get_stripped_from_form_dict('%s_parameters' % key)
 506         if parameters:
 507             for par in parameters.split(','):
 508                 par = par.strip()
 509                 if variables.has_key(par):
 510                     value = variables[par]
 511                 else:
 512                     key_parts = par.strip().split('.')
 513                     if len(key_parts) == 2:
 514                         key_parts.insert(0, u'')
 515                     elif len(key_parts) < 2:
 516                         pass  # TODO: raise error - parameter not specified correctly
 517                     dict_name, form_name, field_name = key_parts[:3]
 518                     if not (form_name or dict_name):
 519                         dict_name, form_name = self.dict_name, self.form_name
 520                     elif not dict_name:
 521                         dict_name = self.dict_name
 522                     parameter_form_key = u'%s.%s' % (dict_name, form_name)
 523                     parameter_key = u'%s.%s' % (parameter_form_key, field_name)
 524                     if self.forms.has_key(parameter_form_key):
 525                         value = self.get_field_value(parameter_form_key, field_name, parameter_key, for_action = for_action)
 526                 result.append(value)
 527         return result
 528 
 529             
 530         
 531     def get_stripped_from_form_dict(self, key):
 532         return self.form['dict'].get('%s.%s' % (self.form_name, key), u'').strip('`')
 533 
 534         
 535     def get_stripped_from_connection_dict(self, key):
 536         return self.connection_dict.get('%s.%s' % (self.connection_name, key), u'').strip('`')
 537 
 538         
 539 
 540     # =============================
 541     # Error Message
 542     # =============================
 543     
 544     def error_message(self, message):
 545         formatter = self.formatter
 546         result = [ '', formatter.preformatted(True) ]
 547         message = self.wikiutil.escape(message, 1)
 548         result.append(formatter.rawHTML("Form macro error: %s" % message))
 549         result.append(formatter.preformatted(False))
 550         return u'\n'.join(result)
 551        

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2008-12-28 10:43:23, 22.1 KB) [[attachment:Form-0.10alpha.py]]
  • [get | view] (2010-01-29 18:51:04, 22.7 KB) [[attachment:Form-0.11alpha.py]]
  • [get | view] (2014-06-11 22:22:41, 22.8 KB) [[attachment:Form-0.12alpha.py]]
  • [get | view] (2008-02-04 07:06:07, 20.9 KB) [[attachment:Form-0.8alpha.py]]
  • [get | view] (2008-07-31 14:21:14, 21.8 KB) [[attachment:Form-0.9alpha.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.