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

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.