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

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.